/* clock.cc */

#include "clock.h"
#include "element.h"
#include "component.h"
#include "link.h"
#include "signal_change.h"
#include "root.h"

static list<dc_clock *>clock_list;

static int in_advance = 0;

dc_clock::dc_clock( void ) {
  initial_interval = interval = 1;
  time = 0;
  delta_time = 0;
  global_entry = clock_list.append( this );
}

dc_clock::dc_clock( const string &label, dc_node *parent = nil, 
		    double intrvl = 1 ) {
  time = 0;
  delta_time = 0;
  global_entry = clock_list.append( this );

  initial_interval = interval = intrvl;
  
  set_both( label, parent );
}

dc_clock::~dc_clock( void ) {
  clock_list.del_item( global_entry );

  dc_label *t;
  forall( t, main_targets ) {
    switch( t->sub_type() ) {
    case Element_t :
      ( ( dc_element * )t )->clock = nil;
      ( ( dc_element * )t )->clk_entry = nil;      
      break;
    case Component_t : case Modification_t : case GA_t :
      ( ( dc_component * )t )->clock = nil;
      ( ( dc_component * )t )->clk_entry = nil;      
      break;
    default :;
    }
  }
  main_targets.clear();

  dc_link *l;
  forall( l, links ) {
    l->clock = nil;
    l->clk_entry = nil;
  }
  links.clear();

  forall( t, secondary_targets ) {
    switch( t->sub_type() ) {
    case Element_t :
      ( ( dc_element * )t )->secondary_clocks.
	del_item( ( ( dc_element * )t )->secondary_clocks.search( this ) );
      break;
    case Component_t : case Modification_t : case GA_t :
      ( ( dc_component * )t )->secondary_clocks.
	del_item( ( ( dc_component * )t )->secondary_clocks.search( this ) );
      break;
    default:;
    }
  }
  secondary_targets.clear();
  
  dc_clock *clk;
  forall( clk, clocks ) {
    clk->rev_clocks.del_item( clk->rev_clocks.search( this ) );
  }
    clocks.clear();

  forall( clk, rev_clocks ) {
    clk->clocks.del_item( clk->clocks.search( this ) );
  }
}

double dc_clock::advance( void ) {
  return advance( interval );
}

double dc_clock::advance( double d_time ) {
  //if( status != Active_s ) return 0;

  in_advance++;

  dc_trace( TRACE_MANY ) {
    cout << "dc_clock::advance -- " << full_type() << " advancing by "
	 << d_time << "\n";
  }

  double target_time = time + d_time;
  while( time < target_time ) {
    update();

    /* query interval after update to allow descended classes use virtual update
       to control step */
    double t_interval = interval;
    time += t_interval;
    delta_time = t_interval;

    dc_clock *clk;
    forall( clk, clocks ) clk->advance( t_interval );
    
    dc_label *l;
    forall( l, main_targets ) {
      switch( l->sub_type() ) {
      case Element_t :
	( ( dc_element * )l )->update();
	break;
      case Component_t :
//	cerr << "UPDATE OF " << l->full_type() << " BY " << full_type() << "\n";
	( ( dc_component * )l )->update();
	break;
      default :
	dc_trace( TRACE_ERROR ) {
	  cerr << "dc_clock::advance -- " << full_type() 
	       << " failed to advance unclocked " << l->full_type() << "\n";
	}
      }
    }
    forall( l, secondary_targets ) {
      switch( l->sub_type() ) {
      case Element_t :
	( ( dc_element * )l )->refresh();
	break;
      default :
	dc_trace( TRACE_ERROR ) {
	  cerr << "dc_clock::advance -- " << full_type() 
	       << " failed to advance unclocked " << l->full_type() << "\n";
	}
      }
    }

    dc_trace( TRACE_ALL ) {
      cerr << "dc_clock::advance -- " << full_type() << " advanced by " 
	   << t_interval << " to " << time << "\n";
    }
  }

  dc_link *l;
  forall( l, links ) l->exec();

  in_advance--;
  if( !in_advance ) root.end_advance();
  return d_time;
}

void dc_clock::reset( void ) {
  time = 0;
  delta_time = 0;
  interval = initial_interval;
  dc_label *l;
  forall( l, main_targets ) {
    switch( l->sub_type() ) {
    case Element_t :
      ( ( dc_element * )l )->reset();
      break;
    case Component_t : case Modification_t : case GA_t :
      ( ( dc_component * )l )->reset();
      break;
      /*    case Clock_t :
      ( ( dc_clock * )l )->reset();
      break;*/
    default :;
    }
  }

  dc_clock *clk;
  forall( clk, clocks ) clk->reset();
}

bool dc_clock::add( dc_label &l ) {
  switch( l.sub_type() ) {
  case Element_t :
    if( ( ( dc_element * )&l )->clock != nil ) {
      ( ( dc_element * )&l )->clock->remove( l );
    }
    ( ( dc_element * )&l )->clock = this;
    ( ( dc_element * )&l )->clk_entry = main_targets.append( &l );
    break;
  case Component_t : case GA_t :
    if( ( ( dc_component * )&l )->clock != nil ) {
      ( ( dc_component * )&l )->clock->remove( l );
    }
    ( ( dc_component * )&l )->clock = this;
    ( ( dc_component * )&l )->clk_entry = main_targets.append( &l );
    break;
  case Clock_t :
    return add( *( ( dc_clock * )&l ) );
    break;;
  default : return true;
  }
  return false;
}

bool dc_clock::remove( dc_label &l ) {
  switch( l.sub_type() ) {
  case Element_t :
    if( ( ( dc_element * )&l )->clock != this ) {
      return true;
    }
    ( ( dc_element * )&l )->clock = nil;
    main_targets.del_item( ( ( dc_element * )&l )->clk_entry );
    ( ( dc_element * )&l )->clk_entry = nil;
    break;
  case Component_t : case Modification_t : case GA_t :
    if( ( ( dc_component * )&l )->clock != this ) {
      return true;
    }
    ( ( dc_component * )&l )->clock = nil;
    main_targets.del_item( ( ( dc_component * )&l )->clk_entry );
    ( ( dc_component * )&l )->clk_entry = nil;
    break;
  case Clock_t :
    return remove( *( ( dc_clock * )&l ) );
  default : return true;
  }
  return false;
}

bool dc_clock::add( dc_link &l ) {
  if( l.clock == this ) return true;
  if( l.clock ) {
    l.clock->remove( l );
  }
  l.clock = this;
  l.clk_entry = links.append( &l );
  return false;
}

bool dc_clock::remove( dc_link &l ) {
  if( l.clock != this ) return true;
  
  l.clock = nil;
  links.del_item( l.clk_entry );
  l.clk_entry = nil;
  return false;
}

bool dc_clock::add( dc_clock &l ) {
  clocks.append( &l );
  l.rev_clocks.append( this );
  return false;
}

bool dc_clock::remove( dc_clock &l ) {
  list_item li = clocks.search( &l );
  if( li == nil ) return true;
  clocks.del_item( li );
  return false;
}

bool dc_clock::add_secondary( dc_label &l ) {
  switch( l.sub_type() ) {
  case Element_t :
    if( ( ( dc_element * )&l )->secondary_clocks.search( this ) != nil )
      return true;
    ( ( dc_element * )&l )->secondary_clocks.append( this );
    secondary_targets.append( &l );
    break;
  case Component_t : case GA_t :
    if( ( ( dc_component * )&l )->secondary_clocks.search( this ) != nil )
      return true;
    ( ( dc_component * )&l )->secondary_clocks.append( this );
    secondary_targets.append( &l );   
    break;
  default : return true;
  }
  return false;
}

bool dc_clock::remove_secondary( dc_label &l ) {
  list_item li;
  switch( l.sub_type() ) {
  case Element_t :
    li = ( ( dc_element * )&l )->secondary_clocks.search( this );
    if( li == nil );
      return true;
    ( ( dc_element * )&l )->secondary_clocks.del_item( li );
    li = secondary_targets.search( &l );
    secondary_targets.del_item( li );
    break;
  case Component_t : case Modification_t : case GA_t :
    li = ( ( dc_component * )&l )->secondary_clocks.search( this );
    if( li == nil );
      return true;
    ( ( dc_component * )&l )->secondary_clocks.del_item( li );
    li = secondary_targets.search( &l );
    secondary_targets.del_item( li );
    break;
  default : return true;
  }
  return false;
}


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

//   if( set ) { /* FREEZE */
//     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;
//       return;
//     }
//   } else { /* 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;
// 	break;
//       default : 
// 	return;
//       }
//     }
//   }
//   signal_status_change( this, old_status );
// }

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

//   if( set ) { /* MAKE DORMANT */
//     switch( status ) {
//     case Active_s :
//       status = Dormant_s;
//       break;
//     case Frozen_s :
//       status = Dormant_Frozen_s;
//       break;
//     default : 
//       if( !inherited ) 
// 	dormant_inh = false;
//       return;
//     }
//     dormant_inh = inherited;
//   } else { /* MAKE NON_DORMANT */
//     /* only make non-dormant if the non-dormant status is !inherited or the
//        dormant 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;
//       }
//     }
//   }
//   signal_status_change( this, old_status );
// }

dc_label *dc_clock::duplicate( dc_node *parent ) const {
  dc_clock *dupe = new dc_clock();

  if( !is_temporary() ) {
    if( dupe->set_both( local_label(), parent ) ) {
      delete( dupe );
      return nil;
    }
  }
  dupe->set_visible( is_visible() );
  
  cerr << "DUPLICATE CLOCK NOT WRITTEN YET\n";
  exit( 1 );

  return ( dc_label * )dupe;
}

void reset_all( void ) {
  dc_clock *clk;
  forall( clk, clock_list ) {
    clk->reset();
  }
}
