/* ga_input.cc */

#include "ga_input.h"
#include <math.h>
#include "StdBitString.hh"
#include "id_lookup.h"
#include "element.h"

static const double ln2 = log(2);

inline long bound( long min, long max, long i ) {
  return ( i < min ) ? min : ( ( i > max ) ? max : i );
}

bool ga_el_input::find_input( dc_label *origin ) {
  input = ( dc_element * )t_search( path, Element_t, origin );
  if( input == nil ) return true;
  return ( input->get_rtype() != type() );
}

bool ga_bool_input::get( StdBitString &bstr, int off ) const {
  if( bstr.Size() < off ) return true;
  if( val ) bstr.Set(off); else bstr.Clear(off);
  return false;
}

bool ga_bool_input::set( const StdBitString &bstr, int off ) {
  if( bstr.Size() < off ) return true;
  val = bstr[off];
  if( input == nil ) return true;
  input->set( *( new dc_boolean( val ) ) );
  return false;
}

bool ga_int_input::get( StdBitString &bstr, int off ) const {
  return bstr.SetInt( val, off, nbits );
}

bool ga_int_input::set( const StdBitString &bstr, int off ) {
  if( bstr.Size() < off + nbits - 1 ) return true;
  val = bstr.GetInt( off, nbits );
  if( val > range ) val = range - 1;
  if( input == nil ) return true;
  
  dc_int *old_val = ( dc_int * )input->get_previous();
  dc_int *new_val = new dc_int( ( long )get_int() );
  new_val->set_units( old_val->get_units() );
  input->set( *new_val );

  //  cerr << "Set input " << path << " to " << get_int() << "\n";
  return false;
}

void ga_int_input::set_int( long int i ) { 
  val = ( long )( bound( 0, range, i - min ) );
}

bool ga_int_input::set_range( long int n, long int x ) {
  if( x <= n ) {
    return true;
  }
  min = n;
  nbits = ( int )ceil( log( x - n + 1 ) / ln2 );
  range = ( 1 << nbits ) - 1;
  if( val > range ) val = range;

  return false;
}

ostream &ga_int_input::display( ostream &s = cout ) const {
  return s << path << "( min = " << min << ", max = " << min + range << ", nbits = " << nbits << " ) = " << get_int();
}

bool ga_real_input::get( StdBitString &bstr, int off ) const {
  return bstr.SetInt( val, off, nbits );
}

bool ga_real_input::set( const StdBitString &bstr, int off ) {
  if( bstr.Size() < off + nbits - 1 ) return true;
  val = bstr.GetInt( off, nbits );
  if( val > range ) val = range;
  if( input == nil ) return true;

  dc_real *old_val = ( dc_real * )input->get_previous();
  dc_real *new_val = new dc_real( get_real() );
  new_val->set_units( old_val->get_units() );
  input->set( *new_val );

  //  cerr << "Set input " << path << " to " << get_real() << "\n";
  return false;
}

bool ga_real_input::set_range( double n, double x, int nb ) {
  if( x <= n ) {
    return true;
  }
  min = n;
  nbits = nb;
  range = ( 1 << nbits ) - 1;
  div = ( x - n ) / range;
  if( val > range ) val = range;

  //  cerr << "ga input \"" << *i << "\n";
  return false;
}

bool ga_real_input::set_range( double n, double x, double resolution ) {
  if( x <= n ) {
    return true;
  }
  min = n;

  nbits = ( int )ceil( log( ( x - n ) / resolution ) / ln2 );
  range = ( 1 << nbits ) - 1;
  div = ( x - n ) / range;

  if( val > range ) val = range;
  return false;
}

double ga_real_input::get_real( void ) const {
  return min + val * div;
}

ostream &ga_real_input::display( ostream &s = cout ) const {
  return s << path << "( min = " << min << ", max = " << min + range * div << ", nbits = " << nbits << " ) = " << get_real();
}

void ga_real_input::set_real( double d ) {
  val = ( long )( ( d - min ) / div );
  dc_real *old_val = ( dc_real * )input->get_previous();
  dc_real *new_val;
  if( old_val ) {
    new_val = new dc_real( d );
    new_val->set_units( old_val->get_units() );
  } else {
    new_val = new dc_real( d );
  }
  input->set( *new_val );
}

void ga_real_input::set_real_normalized( double d ) {
  val = ( long )( d * range );
  dc_real *old_val = ( dc_real * )input->get_previous();

  dc_real *new_val;  
  if( old_val ) {
    new_val = new dc_real( get_real() );
    new_val->set_units( old_val->get_units() );
  } else {
    new_val = new dc_real( get_real() );
  }
  input->set( *new_val );
  //  cerr << "Set " << path << " to " << get_real() << "\n";
}

bool ga_real_input::refresh( void ) {
  dc_real *r = ( dc_real * )input->get_previous();
  if( r == nil ) return true;

  val = ( long )( ( r->get() - min ) / div );
  
  return false;
}

void ga_input_list::add_input( ga_input &i ) {
  inputs.append( &i );
  //  string label_name = i.get_path();
  //  int e = label_name.length() - 1;
  //  for( int n = e ; n > 0 && label_name[n] != ID_SEPARATOR ; n-- );
  //  dc_arg *arg = new dc_arg( label_name( n, e ), i.type() );
  nbits += i.nBits();
}

void ga_input_list::clear( void ) {
  ga_input *i;
  forall( i, inputs ) delete( i );
  inputs.clear();
  nbits = 0;
}

void ga_input_list::get( StdBitString &bstr ) const {
  bstr.Resize( nbits );
  
  int b = 0;
  ga_input *i;
  forall( i, inputs ) {
    i->get( bstr, b );
    b += i->nBits();
  }
}

bool ga_input_list::set( const StdBitString &bstr ) {
  if( bstr.Size() != nbits ) return true;
  
  int b = 0;
  ga_input *i;
  forall( i, inputs ) {
    i->set( bstr, b );
    b += i->nBits();
  }

  return false;
}

void ga_input_list::forall_inputs( void fn( const ga_input & ) ) const {
  ga_input *i;
  forall( i, inputs ) {
    fn( *i );
  }
}

void ga_input_list::forall_inputs( void fn( const ga_input &, void * ), 
				   void *vp ) const {
  ga_input *i;
  forall( i, inputs ) {
    fn( *i, vp );
  }
}

ostream &ga_input_list::display( ostream &s = cout ) const {
  list_item li = inputs.first();
  if( li ) {
    s << "( " << *( inputs.inf( li ) );
    while( ( li = inputs.succ( li ) ) != nil ) {
      s << ", \n";
      tab_in();
      s << *( inputs.inf( li ) );
    }
    return s << " )";
  }
  return s;
}

bool ga_input_list::find_inputs( dc_label *origin ) {
  ga_input *i;
  bool fail = false;
  forall( i, inputs ) {
    if( i->find_input( origin ) ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "ga_input_lists::find_inputs -- failed to find \"" 
	     << i->get_path() << "\" from " 
	     << ( origin ? origin->full_type() : string( "root" ) ) << "\n";
      }
      fail = true;
    }
  }
  return fail;
}
