/* node.cc */

#include "root.h"
#include "component.h"
#include "signal_change.h"
#include "id_lookup.h"

dc_node::dc_node( void ) {
  localTag = 0;
}

dc_node::dc_node( cstring label, dc_node *parent = nil ) {
  localTag = 0;

  set_both( label, parent );
}

dc_node::~dc_node( void ) {
  /* delete children */
  dic_item child_it;
  forall_items( child_it, child_list ) {
    dc_label *child = child_list.inf( child_it );
    delete( child );
  }
  child_list.clear();
}

void dc_node::expand( list<dc_label *> &l ) const {
  dic_item it;
  forall_items( it, child_list ) {
    l.append( child_list.inf( it ) );
  }
}

dc_label *dc_node::lookup_child( cstring label, 
				  const bool see_all = false ) const {
  dic_item child_it = child_list.lookup( label );
  if( child_it == nil )
    return nil;
  dc_label *id = child_list.inf( child_it );
  if( see_all || search_eligible( id ) )
    return id;
  return nil;
}

bool dc_node::child_exists( cstring label ) const {
  dic_item child_it = child_list.lookup( label );
  return ( child_it != nil );
}

bool dc_node::add_child( dc_label *child ) {
  /* make sure not adding child as child of descendant */
  if( child->type() == Node_t ) {
    dc_node *check = this;
    while( check ) {
      if( check == child ) {
	dc_trace( TRACE_ERROR ) {
	  cerr << "dc_node::add_child -- adding \"" << child->local_label()
	       << "\" as child of " << full_type() 
	       << " would result in component loop\n";
	}
	return true;
      }
      check = check->get_parent();
    }
  }

  if( child_list.lookup( child->local_label() )!= nil ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "dc_node::add_child -- " << full_type() 
	   << " already has a child with label " << local_label() << "\n";
    }
    return true;
  }
  
  child_list.insert( child->local_label(), child );

  child_init( child );
  return false;
}

bool dc_node::remove_child( cstring label ) {
  dic_item di;
  di = child_list.lookup( label );
  if( di == nil ) {
    dc_trace( TRACE_WARNING ) {
      cerr << "dc_node::remove_child -- child not found in parent's list\n";
    }
    return true;
  }
  child_cleanup( child_list.inf( di ) );
  child_list.del_item( di );

  return false;
}

void dc_node::set_visible_i( const bool v ) {
  set_visible( v );
  dic_item c;
  forall_items( c, child_list ) {
    child_list.inf( c )->set_visible_i( v );
  }
}

void dc_node::for_children( void operation( dc_label * ), 
			     const bool see_all = false ) {
  dic_item di;

  forall_items( di, child_list ) {
    dc_label *id = child_list.inf( di );
    if( see_all || search_eligible( id ) )
      operation( id );
  }
}

void dc_node::for_children( void operation( dc_label *, void * ), 
			     void *vp, const bool see_all = false ) {
  dic_item di;

  forall_items( di, child_list ) {
    dc_label *id = child_list.inf( di );
    if( see_all || search_eligible( id ) )
      operation( id, vp );
  }
}

void dc_node::for_children( void operation( const dc_label * ), 
			     const bool see_all = false ) const {
  dic_item di;

  forall_items( di, child_list ) {
    dc_label *id = child_list.inf( di );
    if( see_all || search_eligible( id ) )
      operation( id );
  }
}

void dc_node::for_children( void operation( const dc_label *, void * ), 
			     void *vp, const bool see_all = false ) const {
  dic_item di;

  forall_items( di, child_list ) {
    dc_label *id = child_list.inf( di );
    if( see_all || search_eligible( id ) )
      operation( id, vp );
  }
}

/* duplicate children */
void dup_child( dc_label *child, void *dupe ) {
  dc_label *dupe_child = child->duplicate( ( dc_node * )dupe );
  if( dupe_child )
    dupe_child->set_templated( true );
}

dc_label *dc_node::duplicate( dc_node *parent ) const {
  dc_node *dupe = new dc_node();
  if( !is_temporary() ) {
    if( dupe->set_both( local_label(), parent ) ) {
      delete( dupe );
      return nil;
    }
  }
  dupe->set_visible( is_visible() );
  
  /* duplicate sub-elements and sub-components */
  for_children( dup_child, ( void * )dupe, true );

  return ( dc_label * )dupe;
}

/* functions to perform an operation on all labels mathching a given set of 
   criteria */
#include <LEDA/queue.h>

void for_desc_match( bool criterion( dc_label * ), dc_label *origin, 
		     void op( dc_label * ), const bool see_all = false ) {
  if( !origin ) { 
    for_all_match( criterion, op, see_all );
    return;
  }
  
  queue<dc_label *> label_q;
  label_q.append( origin );
  
  while( !label_q.empty() ) {
    dc_label *id = label_q.pop();
    if( id->type() == Node_t ) {
      dic_item di;
      forall_items( di, ( ( dc_node * )id )->child_list ) {
	label_q.append( ( ( dc_node * )id )->child_list.inf( di ) );
      }
    }

    if( ( see_all || search_eligible( id ) ) && criterion( id ) )
      op( id );
  }
}

void for_desc_match( bool criterion( dc_label *, void * ), 
		     dc_label *origin, void op( dc_label *, void * ), 
		     void *veep, const bool see_all = false ) {
  if( !origin ) {
    for_all_match( criterion, op, veep, see_all );
    return;
  }

  queue<dc_label *> label_q;
  label_q.append( origin );

  while( !label_q.empty() ) {
    dc_label *id = label_q.pop();
    if( id->type() == Node_t ) {
      dic_item di;
      forall_items( di, ( ( dc_node * )id )->child_list ) {
	label_q.append( ( ( dc_node * )id )->child_list.inf( di ) );
      }
    }

    if( ( see_all || search_eligible( id ) ) && criterion( id, veep ) )
      op( id, veep );
  }
}

void for_all_match( bool criterion( dc_label * ), void op( dc_label * ), 
		    const bool see_all = false ) {
  dic_item di;
  forall_items( di, root.tag_list ) {
    dc_label *id = root.tag_list.inf( di );
    if( ( see_all || search_eligible( id ) ) && criterion( id ) )
      op( id );
  }
}

void for_all_match( bool criterion( dc_label *, void * ), 
		      void op( dc_label *, void * ), void *veep, 
		      const bool see_all = false ) {
  dic_item di;
  forall_items( di, root.tag_list ) {
    dc_label *id = root.tag_list.inf( di );
    if( ( see_all || search_eligible( id ) ) && criterion( id, veep ) )
      op( id, veep );
  }
}

void for_root_match( bool criterion( dc_label * ), void op( dc_label * ), 
		     const bool see_all = false ) {
  dic_item di;
  forall_items( di, root.label_list ) {
    dc_label *id = root.label_list.inf( di );
    if( ( see_all || search_eligible( id ) ) && criterion( id ) )
      op( id );
  }
}

void for_root_match( bool criterion( dc_label *, void * ), 
		     void op( dc_label *, void * ), void *veep, 
		     const bool see_all = false ) {
  dic_item di;
  forall_items( di, root.label_list ) {
    dc_label *id = root.label_list.inf( di );
    if( ( see_all || search_eligible( id ) ) && criterion( id, veep ) )
      op( id, veep );
  }
}

void for_type_match( bool criterion( dc_label * ), dc_label *origin, 
		     user_type u_type, void op( dc_label * ), 
		     const bool see_all = false ) {
  queue <dc_label *> search_q; /* queue whos decendents are to be searched but
				  not operated on */
  queue <dc_label *> type_q;
  if( origin ) {
    if( origin->sub_type() == Component_t && 
	( ( dc_component * )origin )->is_u_type( u_type ) )
      type_q.append( origin );
    else
      search_q.append( origin );
  } else {
    dic_item di;
    forall_items( di, root.label_list ) {
      dc_label *id = root.label_list.inf( di );
      if( id->sub_type() == Component_t && 
	  ( ( dc_component * )id )->is_u_type( u_type ) ) {
	type_q.append( id );
      } else {
	search_q.append( id );
      }
    }
  }
  
  while( !( search_q.empty() && type_q.empty() ) ) {
    while( !search_q.empty() ) {
      dc_label *id = search_q.pop();
      if( id->type() == Node_t ) {
	dic_item di;
	forall_items( di, ( ( dc_node * )id )->child_list ) {
	  dc_label *child = ( ( dc_node * )id )->child_list.inf( di );
	  if( child->sub_type() == Component_t ) {
	    if( ( ( dc_component * )child )->is_u_type( u_type ) )
	      type_q.append( child );
	    else 
	      search_q.append( child );
	  } else {
	    search_q.append( child );
	  }
	}
      }
    }
    while( !type_q.empty() ) {
      dc_label *id = type_q.pop();
      if( id->type() == Node_t ) {
	dic_item di;
	forall_items( di, ( ( dc_node * )id )->child_list ) {
	  dc_label *child = ( ( dc_node * )id )->child_list.inf( di );
	  if( child->sub_type() == Component_t ) {
	    if( ( ( dc_component * )child )->is_u_type( u_type ) )
	      type_q.append( child );
	    else 
	      search_q.append( child );
	  } else {
	    type_q.append( child );
	  }
	}
      }
      if( ( see_all || search_eligible( id ) ) && criterion( id ) ) {
        op( id );
      }
    }
  }
}

void for_type_match( bool criterion( dc_label *, void * ), 
		     dc_label *origin, user_type u_type, 
		     void op( dc_label *, void * ), void *veep, 
		     const bool see_all = false ) {
    queue <dc_label *> search_q; /* queue whos decendents are to be searched but
				  not operated on */
  queue <dc_label *> type_q;
  if( origin ) {
    if( origin->sub_type() == Component_t && 
	( ( dc_component * )origin )->is_u_type( u_type ) )
      type_q.append( origin );
    else
      search_q.append( origin );
  } else {
    dic_item di;
    forall_items( di, root.label_list ) {
      dc_label *id = root.label_list.inf( di );
      if( id->sub_type() == Component_t && 
	  ( ( dc_component * )id )->is_u_type( u_type ) ) {
	type_q.append( id );
      } else {
	search_q.append( id );
      }
    }
  }
  
  while( !( search_q.empty() && type_q.empty() ) ) {
    while( !search_q.empty() ) {
      dc_label *id = search_q.pop();
      if( id->type() == Node_t ) {
	dic_item di;
	forall_items( di, ( ( dc_node * )id )->child_list ) {
	  dc_label *child = ( ( dc_node * )id )->child_list.inf( di );
	  if( child->sub_type() == Component_t ) {
	    if( ( ( dc_component * )child )->is_u_type( u_type ) )
	      type_q.append( child );
	    else 
	      search_q.append( child );
	  } else {
	    search_q.append( child );
	  }
	}
      }
    }
    while( !type_q.empty() ) {
      dc_label *id = type_q.pop();
      if( id->type() == Node_t ) {
	dic_item di;
	forall_items( di, ( ( dc_node * )id )->child_list ) {
	  dc_label *child = ( ( dc_node * )id )->child_list.inf( di );
	  if( child->sub_type() == Component_t ) {
	    if( ( ( dc_component * )child )->is_u_type( u_type ) )
	      type_q.append( child );
	    else 
	      search_q.append( child );
	  } else {
	    type_q.append( child );
	  }
	}
      }
      if( ( see_all || search_eligible( id ) ) && criterion( id, veep ) ) {
        op( id, veep );
      }
    }
  }
}
