/* component.cc */

#include "id.h"
#include "component.h"
#include "signal_change.h"
#include "link.h"
#include "element.h"
#include "cor_syn_iface.h"
#include "root.h"

dc_component::dc_component( void ) {
  template_ptr = nil;
  template_rehash_done = false;
  template_hash_li = nil;

  lgraph_node = link_graph.new_node( this );
}

dc_component::dc_component( const string &label, dc_component *parent = nil ) {
  template_ptr = nil;
  template_rehash_done = false;
  template_hash_li = nil;

  set_both( label, ( dc_node * )parent );

  dc_trace( TRACE_SOME ) {
    cout << "created component " << label << "\n";
  }

  lgraph_node = link_graph.new_node( this );
}

dc_component::~dc_component( void ) {
  dc_trace( TRACE_SOME ) {
    cout << "deleted component " << full_type() << "\n";
  }

  if( template_hash_li ) {
    root.template_hash_list.erase( template_hash_li );
    template_hash_li = nil;
  }

  edge e;
  while( ( e = link_graph.first_adj_edge( lgraph_node ) ) != nil ) {
    delete( link_graph.inf( e ) ); /* should remove e and rev_edge if any */
  }
  
  link_graph.del_node( lgraph_node );
  if( clock ) clock->remove( *( ( dc_label * )this ) );

  dc_clock *clk;
  forall( clk, secondary_clocks ) {
    list_item it = clk->secondary_targets.search( this );
    clk->secondary_targets.del_item( it );
  }
  secondary_clocks.clear();
}

bool dc_component::update( void ) {
  return false;
}

void inherit_utype( dc_label *id, void *type ) {
  if( id->sub_type() == Component_t ) {
    ( ( dc_component * )id )->set_u_type( *( ( user_type * )type ), true );
  }
}

bool dc_component::set_u_type( const user_type t, const bool I = false ) {
  bool result = uts.set_type( t, I );
  if( I )
    for_children( inherit_utype, ( void * )&t );
  return result;
}

bool dc_component::set_u_type( const string &type_label, const bool I = false ){
  user_type u_type = define_u_type( type_label );
  if( u_type == Undefined_Type ) return true;
  return set_u_type( u_type, I );
}

/* bool dc_component::unset_u_type( string type_label ) {
   user_type u_type = parse_u_type( type_label );
   if( u_type == Undefined_Type ) return true;
   return unset_u_type( u_type );
   } */

void dc_component::set_frozen( bool set, tag inheritance_src ) {
  dc_status old_status = status;

  if( set ) {
    switch( status ) {
    case Active_s :
      status = Frozen_s;
      frozen_inh = inheritance_src;
      break;
    case Dormant_s :
      status = Dormant_Frozen_s;
      frozen_inh = inheritance_src;
      break;
    default : 
      if( frozen_inh != tag_error ) 
	frozen_inh = inheritance_src;
    }
  } else {
    /* unfreeze if inheritedrc is tag_error or if inherited from inherited_src.
       even if not unfrozen pass inheritance src to children to unfreeze */
    if( inheritance_src == tag_error || frozen_inh == inheritance_src ) {
      switch( status ) {
      case Frozen_s :
	status = Active_s;
	break;
      case Dormant_Frozen_s :
	status = Dormant_s;
      default :;
      }
    }  
  }

  if( status != old_status ) 
    signal_status_change( ( dc_label * )this, old_status );

  if( inheritance_src == tag_error ) inheritance_src = get_tag();
  /* call on all children */
  dic_item it;
  forall_items( it, child_list ) {
    dc_label *id = child_list.inf( it );
    switch( id->sub_type() ) {
    case Component_t : case Modification_t : case GA_t :
      ( ( dc_component * )id )->set_frozen( set, inheritance_src );
      break;
    case Element_t :
      ( ( dc_element * )id )->set_frozen( set, inheritance_src );
      break;
    default :;
    }
  }
}

void dc_component::set_dormant( bool set, bool inherited ) {
  dc_status old_status = status;

  if( set ) {
    switch( status ) {
    case Active_s :
      status = Dormant_s;
      dormant_inh = inherited;
      break;
    case Frozen_s :
      status = Dormant_Frozen_s;
      dormant_inh = inherited;
      break;
    default : 
      if( !inherited ) 
	dormant_inh = false;
    } 
  } else {
    /* make non-dormant if change is not inherited or if original status was
       inherited */
    if( dormant_inh || !inherited ) {
      switch( status ) {
      case Dormant_s :
	status = Active_s;
	break;
      case Dormant_Frozen_s :
	status = Frozen_s;
	break;
      default : return; 
      }
    } else return;
  }

  if( status != old_status ) 
    signal_status_change( ( dc_label * )this, old_status );

  /* set all undormant_children */
  dic_item it;
  forall_items( it, child_list ) {
    dc_label *id = child_list.inf( it );
    switch( id->sub_type() ) {
    case Component_t : case Modification_t : case GA_t :
      ( ( dc_component * )id )->set_dormant( set, true );
      break;
    case Element_t :
      ( ( dc_element * )id )->set_dormant( set, true );
      break;
    default :;
    }
  }
}

int dc_component::rehash( void ) {
  if( is_dormant() ) return 0;
  
  /* rehash clock link */
  if( clock_hash_lbl.length() ) {
    dc_clock *c = ( dc_clock * )t_search( clock_hash_lbl, Clock_t, 
					  clock_hash_origin );
    if( c ) c->add( *this );
    else {
      if( clock ) clock->remove( *this );
      return 1;
    }
  }
  return 0;
}

void dc_component::set_template( dc_component *Template ) { 
  if( template_hash_li == nil && Template != nil) {
     template_hash_li = root.template_hash_list.append( this );
  } else if( template_hash_li && Template == nil ) {
    root.template_hash_list.erase( template_hash_li );
    template_hash_li = nil;
  }

  template_ptr = Template;
  template_path = ""; 
}

void dc_component::set_template( cstring tpath ) { 
  if( template_hash_li == nil && tpath.length() ) {
     template_hash_li = root.template_hash_list.append( this );    
  } else if( template_hash_li && tpath.length() == 0 ) {
    root.template_hash_list.erase( template_hash_li );
    template_hash_li = nil;
  }

  template_path = tpath;
  template_ptr = nil;
}

dc_label *dc_component::duplicate( dc_node *parent ) const {
  dc_component *dupe = new dc_component();
  if( !is_temporary() ) {
    if( dupe->set_both( local_label(), parent ) ) {
      delete( dupe );
      return nil;
    }
  }
  dupe->set_visible( is_visible() );
  
  /* type inheritance ? */
  dupe->uts = uts;

  /* inherit template */
  dupe->set_template( template_ptr );

  /* duplicate sub-elements and sub-components */
  for_children( dup_child, ( void * )dupe, true );

  return ( dc_label * )dupe;
}

/* duplicates children whose labels do not already exist in dupe */
void dup_nonexistant_child( dc_label *child, void *dupe ) {
  if( !( ( ( dc_node * )dupe )->child_exists( child->local_label() ) ) ) {
    dc_label *dupe_child;
    if( ( dupe_child = child->duplicate( ( dc_node * )dupe ) ) == nil ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "rehash_templates -- failed to copy " << child->local_label()
	     << " to " << ( ( dc_node * )dupe )->full_type() << "\n";
      }
      return;
    }
    dupe_child->set_templated( true );
  }
}

bool dc_component::rehash_template( void ) {
  if( template_path.length() ) {
    template_ptr = ( dc_component * )t_search( template_path, Component_t, 
					       this, true );
    if( template_ptr == nil ) {
      return true;
    }
  }

  if( template_ptr ) {
    template_ptr->for_children( dup_nonexistant_child, this, true );
  }
  return false;
}

bool dc_component::resolve_template( int maxdepth ) {
  //  if( maxdepth < 0 ) return false;

  dic_item li;
  forall_items( li, child_list ) {
    dc_label *l = child_list.inf( li );
    if( l->sub_type() == Component_t ) {
      if( ( ( dc_component * )l )->resolve_template( maxdepth - 1 ) )
	return true;
    }
  }

  if( !template_rehash_done ) {
    if( rehash_template() ) return true;
    template_rehash_done = true;
  }
  return false;
}

int rehash_templates( void ) {
  dc_component *c;

  int nerrs = 0;

  //  relink_undef_elvalues();

  forall( c, root.template_hash_list ) {
    if( !c->template_rehash_done ) {
      c->resolve_template( root.template_hash_list.length() );
    }
  }

  forall( c, root.template_hash_list ) {
    if( !c->template_rehash_done ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "rehash_templates -- UNRESOLVED TEMPLATE -- " << 
	  c->full_type() << " : ";
	if( c->template_path.length() ) 
	  cerr << c->template_path << "\n";
	else 
	  cerr << ( c->template_ptr ? 
	    c->template_ptr->full_type() : string( "<nil>" ) ) << "\n";
      }
      nerrs++;
    }
  }

  /* components initialized with done = false.  reset to false after each rehash
     so that any changes to template structure will not leave untemplated
     components with true template_rehash_done flags.

     find_unresolved_template then need only check child_components flags
     */
  forall( c, root.template_hash_list ) {
    c->template_rehash_done = false;
  }

  //  relink_undef_elvalues();

  cerr << "REHASH TEMPLATES DONE\n";
  return nerrs;
}

void dc_component::child_init( dc_label *child ) {
  switch( child->sub_type() ) {
  case Component_t : case Modification_t : case GA_t :
    ( ( dc_component * )child )->inherit_u_types( this );
    if( is_dormant() )
      ( ( dc_component * )child )->set_dormant( true, false );
    if( is_frozen() )
      ( ( dc_component * )child )->set_frozen( true, frozen_inh );
    break;
  case Element_t :
    if( is_dormant() )
      ( ( dc_element * )child )->set_dormant( true, false );
    if( is_frozen() )
      ( ( dc_element * )child )->set_frozen( true, frozen_inh );
    break;
  default :;
  }
}
