//=============================================================================================
// Name        : 
// Author      : 
// Revision    : $Revision: #49 $ 
//
// Copyright 2009-2019 ECMWF.
// This software is licensed under the terms of the Apache Licence version 2.0 
// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
// In applying this licence, ECMWF does not waive the privileges and immunities 
// granted to it by virtue of its status as an intergovernmental organisation 
// nor does it submit to any jurisdiction. 
//
// Description : 
//=============================================================================================

#include "node.h"
#include "gui.h"
#include <Xm/ManagerP.h>
#include <Xm/DrawP.h>
#include <algorithm>
#include <typeinfo>
#include <Flag.hpp>

#ifndef node_lister_H
#include "node_lister.h"
#endif

#include "dummy_node.h"
#include "variable_node.h"

#ifndef ecf_node_
#include "ecf_node.h"
#endif

#ifndef globals_H
#include "globals.h"
#endif

#ifndef host_H
#include "host.h"
#endif

#include "trigger_lister.h"
#include "selection.h"
#include "substitute.h"
#include "relation.h"
#include "str.h"
#include "option.h"
#include "array.h"
#include <Suite.hpp>

class node_data {

	xmstring labelTrigger_;
	array<node*> triggered_;
	array<node*> triggers_;
	array<node_info*> info_;

public:
	node_data();
	~node_data();

	const xmstring& labelTrigger() { return labelTrigger_; }
	void labelTrigger(const xmstring&);

	void add_triggered(node*,node*);
	void triggered(trigger_lister&);

	void add(node_info*);
	void remove(node_info*);
	void remove(const str& s) { remove(get(s)); }
	node_info* get(const str&);
};


node_data::node_data():
	labelTrigger_()
{
}

node_data::~node_data()
{
	labelTrigger(xmstring());	
	for(int i = 0; i < info_.count(); i++)
		delete info_[i];
}

void node_data::add(node_info* n)
{
	for(int i = 0; i < info_.count(); i++)
	{
		if(info_[i]->name() == n->name())
		{
			delete info_[i];
			info_[i] = n;
			return;
		}
	}
	info_.add(n);
}

void node_data::remove(node_info *n)
{
	info_.remove(n);
}

node_info* node_data::get(const str& s)
{
	for(int i = 0; i < info_.count(); i++)
		if(info_[i]->name() == s)
			return info_[i];
	return 0;
}


void node_data::labelTrigger(const xmstring& s)
{
	labelTrigger_ = s;
}

void node_data::add_triggered(node* n,node* t)
{
	triggered_.add(n);
	triggers_.add(t);
}

void node_data::triggered(trigger_lister& l)
{
  for(int i = 0; i < triggered_.count(); i++)
    l.next_node(*triggered_[i],0,trigger_lister::normal,triggers_[i]);
}

int node::status() const
{ 
#ifdef BRIDGE
  if (tree_) return tree_->status;  
#endif
  return owner_ ? owner_->status() : STATUS_UNKNOWN;
}

node_data* node::get_node_data()
{
  if(data_ == 0)
    data_ = new node_data();
  return data_;
}

template<class T>
inline node* _node_of(T* n)
{  
  return n ? n->xnode() : 0x0;
}

node::node(host& h,ecf_node* owner)
  : 
  xnode(this)
  ,type_(owner ? owner->type() : NODE_UNKNOWN)
  ,tree_(0) // BRIDGE
  ,next_(0)
  ,kids_(0)
  ,owner_(owner)
  ,host_(h)
  ,folded_(True)
  ,labelTree_()
  ,helper_(0)
  ,data_(0)
  ,triggered_(false)
{
}

node::~node()
{
  // std::cerr << "# node del: " << full_name() << std::endl;
  if (data_) delete data_;
  data_ = 0x0;
}

void node::reset()
{
  if(data_)      
    data_->labelTrigger(xmstring());
  
  labelTree_ = xmstring();
  
  if(kids_) kids_->reset();
  if(next_) next_->reset();
  
  redraw();
}

void node::scan(node* first,node *current)
{
  node *n = current;
  if (n)
  if(n && n->name() != name()) {
    n = first;
    while(n && n->name() != name()) {
      n = n->next_;
    }
  }

  if(n) {
    adopt(n);
    if(kids_) 
      kids_->scan(n->kids_,n->kids_);
  } else {
    create();
    if(kids_) 
      kids_->scan(0,0);
  }

  if(next_) 
    next_->scan(first,n);
}

void node::adopt(node* old)
{
  folded_ = old->folded_;
  old->notify_adoption(this);
  notify_observers();
}

void node::create()
{
}

#ifdef BRIDGE
node_builder* node_builder::builders_[NODE_MAX] = { 0, };

node* node::find(sms_node* n)
{ return (node*) (n ? n->user_ptr : 0x0); }

node::node(host& h,sms_node* owner,char b)
  : xnode(this)
  , type_(owner ? owner->type : NODE_UNKNOWN) 
  , name_ (owner->name ? owner->name : "/")
  , full_name_ (sms_node_full_name(owner))
  , tree_(owner)
  , next_(0)
  , kids_(0)
  , owner_(new ecf_concrete_node<node>((int)0, 0))
  , host_(h)
  , folded_(True)
  , labelTree_()
  , helper_(0)
  , data_(0)
  , triggered_(false)
{
  if (owner) {
    // if (owner->name) name_ = owner->name;
    kids_ = node::create(h,owner->kids,b);
    next_ = node::create(h,owner->next,b);
    owner->user_ptr = this;
  }
}

node* node_builder::build(host& h,sms_node* n,char b)
{
  if(n->type >= 0 && 
     n->type < NODE_MAX && builders_[n->type] != 0) {
    if (builders_[n->type] != 0x0)
      return builders_[n->type]->make(h,n,b);
    else
      std::cerr << "unregistered type " << n->type << "\n";
  }
  return 0;
}

node* node::create(host& h, sms_node* n, char b)
{
  if(!n) return 0x0;
  node* p = node_builder::build(h,n,b);
  return p?p:node::create(h,n->next,b);
}

void node::schanged(sms_node *n,int oldstatus,int oldtryno,int oldflags,void*)
{
  node* p = 0x0;
  if (n) p = (node*) n->user_ptr;
  if(p) {
    try {
      p->update(oldstatus,oldtryno,oldflags);
      p->notify_observers();
      p->redraw();
    } catch (...) {
      printf(" exception in node::changed\n");
    }
  } else {
    if (n->type >= NODE_MAX) return;
    printf("# Got NID for %s",::ecf_node_name(n->type));
    // while(n) {printf("#  %s",n->full_name().c_str()); n = n->parent; }
    printf("# --- \n");
  }
}

#endif

void node::destroy(node* n)
{
  while(n) {
    Widget w = n->widget();
    node*  next = n->next_;
    CompositeWidget c = (CompositeWidget)w;
    if (c) XtUnmanageChildren(c->composite.children,
			      c->composite.num_children);

    destroy(n->kids_);
    n->kids_ = 0x0;
    if (n->owner_) 
      n->owner_->adopt(0x0);    

    delete n;
    n = next;
  }
  // n = 0x0;
}

void node::drawBackground(Widget w,XRectangle* r,bool tree)
{
  if(!tree)
    XClearArea(XtDisplay(w),XtWindow(w),
	       r->x,r->y,r->width,r->height,False);
}

void node::drawNode(Widget w,XRectangle* r,bool tree)
{
	drawBackground(w,r,tree);

	XmString s   = tree ? labelTree() : labelTrigger();
	XmFontList f = smallfont();
        
        XmStringDraw(XtDisplay(w),XtWindow(w),
                     f,
                     s,
                     blackGC(),
                     r->x,
                     r->y+2,
                     r->width,
                     XmALIGNMENT_CENTER, XmSTRING_DIRECTION_L_TO_R, r);
}

void node::sizeNode(Widget w,XRectangle* r,bool tree)
{
	XmString s   = tree ? labelTree() : labelTrigger();
	XmFontList f = smallfont();
	r->width    = XmStringWidth(f,s)  + 4;
	r->height   = XmStringHeight(f,s) + 4;
}

void node::shadow(Widget w,XRectangle *r,bool out)
{
	XmManagerWidget m = (XmManagerWidget)w;
	_XmDrawShadows(XtDisplay(w),XtWindow(w),
                       m->manager.top_shadow_GC,
                       m->manager.bottom_shadow_GC, 
                       r->x,
                       r->y,
                       r->width,
                       r->height,
                       1,out?XmSHADOW_OUT:XmSHADOW_IN);
}


const xmstring& node::labelTree()
{
  if(labelTree_ == 0)
    labelTree_ = make_label_tree();
  return labelTree_;
}

xmstring node::make_label_tree()
{
  return xmstring(name().c_str());
}

const xmstring& node::labelTrigger()
{
  node_data* d = get_node_data();
  if(d->labelTrigger() == 0)
    d->labelTrigger(make_label_trigger());
  return d->labelTrigger();
}

xmstring node::make_label_trigger()
{
  return xmstring(full_name().c_str());
}

void node::append(node* n)
{
  if (!n) return;
  node *k = kids_;
  node *p = 0;
  while(k) {
    p = k;
    k = k->next_;
  }
  if(p) p->next_ = n;
  else  kids_    = n;
}

void node::insert(node* n)
{
  if (!n) return;
  node* k = kids_; kids_ = n; append(k);
}

void node::changed(ecf_node *n,int oldstatus,int oldtryno,int oldflags,void*)
{
  node* p = _node_of(n);
  if (!n) return;
  if(p) {
    try {
      p->update(oldstatus,oldtryno,oldflags);
      p->notify_observers();
      p->redraw();
    } catch (...) { printf(" exception in node::changed\n"); }
  } else {
#ifdef BRIDGE
    if (n->type() >= NODE_MAX) return;
    printf("# Got NID for %s",::ecf_node_name(n->type()));
    while(n) {
      printf("#  %s",n->full_name().c_str());
      n = n->parent();    
    }
    printf("# --- \n");
#endif
  }
}

void node::update(int,int,int)
{
  labelTree_ = xmstring();
  if(data_) data_->labelTrigger(xmstring());
}

Boolean node::visible() const
{
  return True;
}

int node::type()   const { 
  return type_;
}

Boolean node::show_it() const
{
  return this == selection::current_node();
}

const std::string& node::name() const
{
#ifdef BRIDGE
  if (tree_) { return name_; }
#endif
  if (owner_) 
    return owner_->name();
  return ecf_node::no_owner();
}

const std::string& node::full_name() const
{
#ifdef BRIDGE
  if (tree_) { return full_name_; }
#endif
  if (owner_) 
    return owner_->full_name();
  return ecf_node::no_owner();
}

const std::string& node::net_name() const
{
#ifdef BRIDGE
  if (tree_) { static std::string fn = sms_node_full_name(tree_); return fn; }
#endif
  if (owner_) 
    return owner_->full_name();
  return ecf_node::no_owner();
}

Pixel node::color() const
{
  return colors(STATUS_UNKNOWN);
}


void node::search(node_lister& s)
{
  node *n = this;
  while(n) {
    s.next(*n);
    node* k = n->kids();
    if(k) k->search(s);
    n = n->next();
  }
}

std::string node::variable(const std::string& name, bool substitute)
{
  for (node* run = kids(); run; run = run->next())
    if (run->type() == NODE_VARIABLE && run->name() == name) {
      return ((variable_node*) run)->get_var(substitute);
    }
  
  return ecf_node::none();
}

node* node::find(ecf_node* n)
{
  return _node_of(n);
}

static node* finder(const std::string& name, const node* start) {
   node *n = const_cast<node*> (start);
   while (n) {
      if (n->type() == NODE_TRIGGER || n->type() == NODE_COMPLETE) {
         if (n->definition() == name)
            return n;
         else if (n->__node__()->name() == name)
            return n;
         else if (n->__node__()->toString() == name)
            return n;
         ecf_node *owner = n->__node__();
         if (owner) {
            ExpressionWrapper *exp = dynamic_cast<ecf_concrete_node<ExpressionWrapper>*> (owner)
	           ->get();
            if (exp && exp->expression() == name)
               return n;
         }
      }
      node *k = 0;
      if ((k = finder(name, n->kids())))
         return k;
      n = n->next();
   }
   return 0;
}

node* node::find_trigger(const std::string& name) const
{
  node* k = finder(name, this);
  return k ? k : &dummy_node::get(name);
}

node* node::find_limit(const std::string& path, const std::string& name)
{
   node *f = this;
   // if (!strncmp("/", path.c_str(), 1))
   if (!path.empty() && path[0] == '/')
      if (! (f = serv().top()->find(path)))
         return &dummy_node::get(path + ":" + name);

   for (node *n = f->kids(); n != 0; n = n->next()) {
      if (n->type() == NODE_LIMIT && n->name() == name)
         return n;
   }

   for (node *p = f->parent()->kids(); p != 0; p = p->next()) {
      if (p->type() == NODE_FAMILY || p->type() == NODE_TASK || p->type() == NODE_SUITE)
         if (p->name() == path.substr(0, p->name().size())) {
            std::string::size_type next = path.find('/');
            if (next != std::string::npos)
               return p->find_limit(path.substr(next+1, path.size()), name);
         }
   }

   return &dummy_node::get(path + ":" + name);
}

// Trigger proccessing

struct triggered_lister : public trigger_lister {
  node*           n_;
public:
  triggered_lister(node* n) : n_(n) {}
  
  void next_node(node& n,node*,int,node* t) 
  { n.add_triggered(n_,t); }
};

void node::add_triggered(node* n,node* t)
{
  if(data_ == 0) data_ = new node_data();
  data_->add_triggered(n,t);
}

void node::gather_triggered(node* p)
{
  while(p) {
    triggered_lister tl(p);
    p->triggers(tl);
    p->triggered_ = true;
    gather_triggered(p->kids());
    p = p->next();
  }
}

struct kids_triggered_lister : public trigger_lister {
  trigger_lister& l_;
  node* k_;
  node* n_;
public:
  kids_triggered_lister(node *n, node* k,trigger_lister& l):
     l_(l), k_(k), n_(n)  {}
  
  void next_node(node& n,node* p,int,node* t) {
    if(!n.is_my_parent(n_))
      l_.next_node(n,k_,trigger_lister::child,t);
  }
};

static void triggered_by_kids(node* n,node *k,trigger_lister& l)
{
  while(k) {
    kids_triggered_lister ktl(n,k,l);
    k->triggered(ktl);
    triggered_by_kids(n,k->kids(),l);
    k = k->next();
  }
}

struct parent_triggered_lister : public trigger_lister {
  node* n_;
  node* p_;
  trigger_lister& l_;
public:
  parent_triggered_lister(node *n, node* p,trigger_lister& l):
    n_(n), p_(p), l_(l) {}
  
  void next_node(node& n,node* p,int,node* t) {
    l_.next_node(n,p_,trigger_lister::parent,t);
  }
};

static void triggered_by_parent(node* n,node *p,trigger_lister& l)
{
  while(p) {
    parent_triggered_lister ptl(n,p,l);
    p->triggered(ptl);
    p = p->parent();
  }
}

void node::triggered(trigger_lister& l)
{       
  if(!triggered_) // Scan all tree
    gather_triggered(serv().top());
  
  if(data_) data_->triggered(l);
  
  if(l.kids()) triggered_by_kids(this,kids(),l);
  if(l.parents()) triggered_by_parent(this,parent(),l);
}

void node::triggers(trigger_lister&)
{
}

//============================================================

const std::vector<std::string>& node::messages() const 
{ 
#ifdef BRIDGE
  if (tree_) return serv().messages(*this);
#endif
  return serv().messages(*this);  
} 

//============================================================
node* node_find(node* n, std::string path) {
  std::string::size_type pos = path.find("/");
  std::string::size_type beg = 0;
  if (!n) return n;
  while (path[beg] == '/') ++beg;
  node *kid = n->kids();
  while (kid) {
    if (kid->type() != NODE_SUITE && 
	kid->type() != NODE_FAMILY && 
	kid->type() != NODE_TASK ) {kid = kid->next(); continue;}
    if (kid->name() == path.substr(beg, pos-beg)) {
      if (pos ==  std::string::npos)
	return kid;
      else 
	return node_find(kid, path.substr(pos));
    }
    kid = kid->next();      
  }
  return kid;
}

node* node::find(const std::string name) 
{
  node * top = 0x0;
  node_ptr ptr;
  std::string::size_type pos = name.find(":");
  if (pos == std::string::npos) { // not an attribute
    ecf_concrete_node<Defs> * ecfn = 0x0;
    if (0x0 != (top = serv().top())) {
    ecfn = dynamic_cast<ecf_concrete_node<Defs>*>(top->__node__());
    if (0x0 != ecfn) // ok with a node, NOK with attribute
      try {
	if (const_cast<Defs*>(ecfn->get()))
	  ptr = const_cast<Defs*>(ecfn->get())->findAbsNode(name);    
      } catch (...) { 
         fprintf(stderr, "exception with node.cc:find %s\n", name.c_str());
      }
    }
  } else {
    const char* fname = full_name().c_str();
    size_t len1 = name.size(), len2 = strlen(fname);
    if (len1==len2 && !strcmp(name.c_str(), fname)) return this;
    if (len2 < len1 && !strncmp(name.c_str(), fname, len2) 
	&& kids_) return kids_->find(name);
    if (next_) return next_->find(name);
    return 0x0;
  }
  if (0x0 != ptr.get()) {
    return (node*) ptr.get()->graphic_ptr(); 
  } 
  else if (name == "/") 
    return serv().top();

  return node_find(serv().top(), name);
  /*   if (item == 0x0) 
    std::cout << "# not found:" << name << "\n";
    return item; */
}

//============================================================

node* node::parent() const
{  
#ifdef BRIDGE
  if (tree_) if (tree_->parent) return (node*) tree_->parent->user_ptr;
#endif
  if (owner_) { 
    ecf_node *p = owner_->parent();
    return p ? p->xnode() : 0x0;
  }
  return 0x0;
}

const char* node::type_name() const
{
  
  return ecf_node_name(type());	
}

const char* node::status_name() const
{
  return "??";
}

//============================================================

node* node::variableOwner(const char *name)
{
  std::vector<Variable>::const_iterator it;
  node *m = this;
  while(m) {
    { std::vector<Variable> var; m->variables(var);
      for (it = var.begin(); it != var.end(); ++it)
	if (it->name() == name) return m;
    }
    { std::vector<Variable> var; m->genvars(var);
      for (it = var.begin(); it != var.end(); ++it)
	if (it->name() == name) return m;
    }
    m = m->parent();
  }
  return 0;
}

Boolean node::isGenVariable(const char *name) { return False; }

void node::folded(Boolean f)
{ 
  if(f) {
    folded_ = false;
    node *k = kids_;
    while(k) {
      if(k->visible() || k->show_it()) {
	folded_ = true;
	break;
      }
      k = k->next();
    }
  } else 
    folded_ = f;
  
  redraw();
}

void node::why(std::ostream&)
{
}

bool node::evaluate() const
{
  return false;
}

void node::tell_me_why(std::ostream&)
{
}

void node::suspended(std::ostream&)
{
}

void node::aborted(std::ostream&)
{
}

void node::queued(std::ostream&)
{
}

bool node::is_my_parent(node* p) const
{
  const node* n = this;
  while(n) {
    if(n == p)
      return true;
    n = n->parent();
  }
  return false;
}

#include <boost/date_time/posix_time/posix_time.hpp>
void node::info(std::ostream& f)
{
  using namespace boost::posix_time;
  using namespace boost::gregorian;

  f << "name     : " << name() << "\n";
  f << "type     : " << type_name() << "\n";
  f << "status   : " << status_name() << "\n";

  if (owner_) {
    // if (owner_->type() == NODE_TASKtas ) 
    {
       boost::posix_time::ptime state_change_time = owner_->status_time();
       if (!state_change_time.is_special()) {
          f << "at       : " << to_simple_string(state_change_time) << "\n"; // https://software.ecmwf.int/issues/browse/SUP-649
       }
    }
  }
  f << "----------\n";
  //    1234567890
}

const std::string node::toString() const
{ 
#ifdef BRIDGE
  if (tree_) { return sms_node_full_name(tree_); }
#endif
  if (owner_) return owner_->toString();
  return ecf_node::none();
}

node* node::find_match(const char* p) 
{
  if (p == NULL) return 0;
  const char* found = find_name(p);
  if (found == NULL) return 0;
  return find(found);
}

const char* node::find_name(const char* p)
{
  static char name[1024];
  strcpy(name,p);   

  char *q = name;
  
  while(*q && *q != '/') q++;
  if(*q) {
    char* r =  q;
    while(*q && *q != ' ' && *q != '\t') q++;
    *q = 0;
    return r;
  }
  return 0;
}

time_t node::suite_time()
{
  node *xnode = this;
  while(xnode) {
    if(xnode->type() == NODE_SUITE)
      return 0; // FILL
    xnode = xnode->parent();
  }   
  return 0;
}

bool node::match(const char* n)
{
  return strstr(name().c_str(),n) != 0;
}

void node::command(const char* cmd)
{
  serv().command(substitute(cmd));
}

std::string node::substitute(const char* cmd)
{
  try {
    return substitute::scan(cmd,this);
  } catch ( std::exception& e ) {
    return cmd;
  }

}

void node::edit(node_editor&)
{
}

void node::apply(node_editor&)
{
}

node_info* node::get_node_info(const str& s)
{
	return data_?data_->get(s):0;
}

void node::add_node_info(node_info* n)
{
	get_node_data()->add(n);
}

void node::remove_node_info(node_info* n)
{
	if(data_) data_->remove(n);
}

void node::remove_node_info(const str& n)
{
	if(data_) data_->remove(n);
}

const char* node::html_page(url& u)
{
	return "node.html";
}

void node::html_name(FILE* f,url& u)
{
  fprintf(f,"<a href=\"%s\">%s</a>",net_name().c_str()+1,name().c_str());
}

void node::html_title(FILE* f,url& u)
{
  if(parent()) parent()->html_title(f,u);
  fprintf(f,"/<a href=\"%s\">%s</a>",net_name().c_str()+1,name().c_str());
}

bool node::is_json = false; // set by url.cc
void node::as_perl(FILE* f,bool full)
{
  if (node::is_json) {
    fprintf(f,"{\n");
  } else 
    fprintf(f,"bless({\n");

  perl_member(f,"name",name());
  perl_member(f,"full", full_name());
  perl_member(f,"status", status());
  perl_member(f,"status_name", status_name());
  
  if(full) perlify(f);
  
  if (node::is_json) {
    fprintf(f,"\"class\": \"%s\" }", perl_class());
  } else fprintf(f,"},'ecf::node::%s')",perl_class()); 
}

void node::perl_member(FILE* f,const char* p,const char* v)
{
  if(v) {
    if (node::is_json) { 
      unsigned int i = 0; char *c; char bak[1024]; strncpy(bak, v, 1024);
      for (c = bak; i<strlen(v) && i<1024; c++, i++) {
	if (*c == '"') *c = '\'';
      }
      fprintf(f,"\"%s\": \"%s\",\n",p,bak); } else 
      fprintf(f,"%s=>'%s',\n",p,v);
  }
}

void node::perl_member(FILE* f,const std::string& p,const std::string&v)
{
  perl_member(f, p.c_str(), v.c_str()); /*
  if (node::is_json) { 
    std::string bak = v;
    for (unsigned int i=0; i<bak.size(); i++) {
      if (bak[i] == '"') bak[i] = '\'';
    }
    fprintf(f,"\"%s\": \"%s\",\n",p.c_str(),bak.c_str()); } else 
    { fprintf(f,"%s=>%s,\n",p.c_str(),v.c_str()); } */
}

void node::perl_member(FILE* f,const char * p, int v)
{
  if (node::is_json) fprintf(f,"\"%s\": \"%d\",\n",p,v); else 
  fprintf(f,"%s=>%d,\n",p,v);
}

void node::perl_member(FILE* f,const char* p,ecf_list* v)
{
  if (node::is_json) {
    fprintf(f,"\"%s\": [\n",p);
    while(v) {
      fprintf(f,"'name': '%s',\n",v->name().c_str());
      v = v->next;
    }
    fprintf(f,"\n],\n");
  return; }

  fprintf(f,"%s=>[\n",p);
  while(v) {
    fprintf(f,"'%s',",v->name().c_str());
    v = v->next;
  }
  fprintf(f,"\n],\n");
} 

static proc_substitute s_full_name("<full_name>",&node::full_name);
static proc_substitute s_node_name("<node_name>",&node::node_name);
static proc_substitute s_parent_name("<parent_name>",&node::parent_name);

void node::check() {
  if (__node__() == 0x0) 
    std::cerr << "# node: no owner: " << name() << "\n";
  if (parent() == 0x0) 
    std::cerr << "# node: no parent: " << name() << "\n";
  node *n;
  for(n = kids(); n; n = n->next())
    { n->check(); }
  if ((n = next())) n->check();
}

bool node::ondemand(bool full) 
{
  // ecf_node *ec = owner_;
  // if (0 == ec) return false;
  // else if (0 != kids()) return false; // gen variables at least 
  // printf("demanding\n");
  // ec->make_subtree(); node *xnode = ec->create_tree(serv()); ec->adopt(xnode); 
  // serv().redraw();
  return false;
}

const std::string& node::parent_name() const 
{
  if (parent())
    return parent()->full_name();
  return ecf_node::none();
}

void node::delvars() {
}

void node::unlink(bool detach) { 
  if (__node__()) __node__()->unlink(detach); 
  for (node *run = kids(); run; run = run->next()) 
    { run->unlink(detach); }
}

void node::remove() {
  node *top = parent();
  if (!top) return;
  node *run = top->kids_;
  
  if (run == this) {
    top->kids_ = this->next_;
  } else
    while (run) {
      if (run->next_ == this) {
	run->next_ = this->next_;
	break;
      }
      run = run->next_;
    }
}
