/* link.cc */

#include "link.h"
#include "root.h"
#include "cor_syn_iface.h"

/* declared extern in link.h */
lgraph link_graph;

/* global list needed :
     so that cleanup is possible
     so that links both of whose ends have been lost can be rehashed
     to allow easy iteration when some are directed and some are undirected
     */
static list<dc_link *> list_o_links;

dc_link::dc_link() {
  lgraph_edge = nil;
  lgraph_rev_edge = nil;
  hash_origin = nil;
  directed = false;
  lol_pos = list_o_links.push( this );

  clock = nil;
  clk_entry = nil;
}

dc_link::~dc_link() {
  if( clock )
    clock->remove( *this );

  if( lgraph_edge ) link_graph.del_edge( lgraph_edge );
  if( lgraph_rev_edge ) link_graph.del_edge( lgraph_rev_edge );
  list_o_links.del_item( lol_pos );
}

bool dc_link::set_ends( dc_component &C_one, dc_component &C_two ) {
  edge new_edge = link_graph.new_edge( C_one.get_node(), C_two.get_node(),
				       this );
  edge new_rev_edge = nil;
  if( !directed ) 
    new_rev_edge = link_graph.new_edge( C_two.get_node(), C_one.get_node(), 
					this );
  
  if( init() ) {
    link_graph.del_edge( new_edge );
    if( new_rev_edge ) link_graph.del_edge( new_rev_edge );
    return true;
  }

  if( lgraph_edge ) link_graph.del_edge( lgraph_edge );
  if( lgraph_rev_edge ) link_graph.del_edge( lgraph_rev_edge );

  lgraph_edge = new_edge;
  lgraph_rev_edge = new_rev_edge;

  return false;
}

int dc_link::relink( void ) {
  int nerrors = 0;
  dc_component *source, *target;
  if( hash_source.length() ) {
    source = ( dc_component * )t_search( hash_source, Component_t, hash_origin);
    if( source == nil ) nerrors++;
  } else {
    source = get_source();
  }
  
  if( hash_target.length() ) {
    target = ( dc_component * )t_search( hash_target, Component_t, hash_origin);
    if( target == nil ) nerrors++;
  } else {
    target = get_target();
  }

  if( lgraph_edge ) link_graph.del_edge( lgraph_edge );
  if( lgraph_rev_edge ) link_graph.del_edge( lgraph_rev_edge );

  node source_node, target_node;
  if( source ) {
    source_node = source->get_node();
  } else {
    if_trc( hash_source.length() != 0, TRACE_WARNING ) {
      cerr << "dc_link::relink -- failed to locate source \"" << hash_source 
	   << "\"\n";
    }
    source_node = nil_node;
  }
  if( target ) {
    target_node = target->get_node();
  } else {
    if_trc( hash_target.length() != 0, TRACE_WARNING ) {
      cerr << "dc_link::relink -- failed to locate target \"" << hash_target 
	   << "\"\n";
    }
    target_node = nil_node;
  }
  
  lgraph_edge = link_graph.new_edge( source_node, target_node, this );
  if( directed )
    lgraph_rev_edge = nil;
  else
    lgraph_rev_edge = link_graph.new_edge( target_node, source_node, this );

  if( source == target )
    nerrors++;

  return nerrors;
}

dc_component *dc_link::get_source( void ) const {
  return lgraph_edge ? link_graph.inf( link_graph.source( lgraph_edge ) ) : nil;
}

dc_component *dc_link::get_target( void ) const {
  return lgraph_edge ? link_graph.inf( link_graph.target( lgraph_edge ) ) : nil;
}

dc_link *allocate_link( dc_link_type lt = Base_lt ) {
  dc_link *new_link;

  switch( lt ) {
  case Base_lt :
    new_link = new dc_link;
    break;
  case Distance_lt :
    new_link = new dc_distance_link;
    new_link->set_clock_info( coriolis_clock_name );
    break;
  case Axial_lt :
    new_link = new dc_axial_link;
    new_link->set_clock_info( coriolis_clock_name );
    break;
  case Attachment_lt :
    new_link = new dc_attachment_link;
    new_link->set_clock_info( coriolis_clock_name );
    break;
  case Force_lt :
    new_link = new dc_force_link;
    new_link->set_clock_info( coriolis_clock_name );
    break;
  default :
    new_link = nil;
  }

  return new_link;
}

dc_link *add_link( dc_component &C_one, dc_component &C_two,
		   dc_link_type type = Base_lt, bool directed = false ) {
  /* create new link */
  dc_link *new_link = allocate_link( type );
  if( new_link == nil ) return nil;

  new_link->set_directed( directed );
  if( new_link->set_ends( C_one, C_two ) ) {
    delete( new_link );
    return nil;
  }
  
  return new_link;
}

dc_link *add_link( const string &src, const string &dest, dc_label *origin,
		   dc_link_type type = Base_lt, bool directed = false ) {
  dc_link *new_link = allocate_link( type );
  if( new_link == nil ) return nil;
  
  new_link->set_directed( directed );
  new_link->set_hash_info( src, dest, origin );

  return new_link;
}

dc_link *add_link( const string &src, dc_label *origin,
		   dc_link_type type = Base_lt, bool directed = false ) {
  dc_link *new_link = allocate_link( type );
  if( new_link == nil ) return nil;
  
  new_link->set_directed( directed );
  new_link->set_hash_info( src, "", origin );
  
  return new_link;
}

void forall_links( void op( dc_link &, void * ), void *vp ) {
  dc_link *l;
  forall( l, list_o_links ) {
    op( *l, vp );
  }
}

void disp_link( dc_link &l, void *stream ) {
  *( ( ostream * )stream ) << l << "\n";
}
  
void display_links( ostream &stream = cout ) {
  forall_links( disp_link, &stream );
}

void clear_link_graph( void ) {
  while( !list_o_links.empty() ) {
    delete( list_o_links.head() );
  }
}
