/* element.h */

#ifndef ELEMENT__H
#define ELEMENT__H

#include "id.h"
#include "data_classes.h"
#include "timed.h"
#include "function.h"
#include "buf_info.h"
#include "component.h"
#include "clock.h"
#include "xtype.h"

#define EXIT_ON_NIL_FUNCTION_RETURN
#define FLAG_NANs

/* - dc_args are a descendant of dc_func.
   - a list of dc_args is kept by each element, and they are used for 
   substitution when an element is called 
   - if substituted with a nil value the args default_val is used */
class dc_arg : dc_func {
private:
  string name;
  dc_func *default_val; /* owned by this object */
  dc_func *value; /* owned by other function */

  dc_type rtype;
  xtype xt;
public:
  dc_arg( cstring name, const dc_type type, dc_func *default_val = nil );
  dc_arg( void ); /* init must be used to set return type and name */

  virtual ~dc_arg( void );

  bool set( dc_func *f );
  
  void set_default( dc_func *DV );
  bool has_default( void ) const { return default_val != nil; }

 /* should be set only once durng parsing */
  void init( cstring N, const dc_type T ) { name = N; rtype = T; }

  dc_data *evaluate_f( void );
  bool match( cstring test_name ) { return ( test_name == name ); }

  string get_name( void ) const { return name; }
  dc_type get_rtype( void ) { return rtype; }
  dc_type get_rtype_c( void ) const { return rtype; }
  void get_xtype( xtype &XT ) { XT = xt; }

  void set_owner( dc_label * );

  ostream &display( ostream &stream = cout ) const;

  /* displays with default value.  non-virtual.*/
  ostream &display_w_default( ostream &stream = cout ) const;
  
  dc_data *simplify( void );

  bool remove_deps( const int, const tag[] );
  int rehash( dep_list &, const tag );

  dc_func *duplicate( dc_label *, list<dc_arg *> * ) const;
};

/* - dc_element is a clocked dc_label
   - dc_elements have an optional evaluation function and an optional list of 
   arguments. these arguments are all dc_args that have optional default 
   functions.
   - if the element has an evaluation function with no args, or if all the args
   have default functions then the element will update its value and old_value
   whenever its main clock advances.
   - otherwise the dc_element can be assigned values

   - an element with args can be called with a list of args.
   - the result of the call will be returned but will not replace the element's
   value or be stored by the element 

   - all elements also have a return type.  whenever the elements evaluation 
   function is set it is checked against this return type and cast if necessary
   - function simplification is also performed by set_func. */
class dc_element : public dc_label, public dc_timed {
private:
  dc_func *eval; /* used to calculate value on update or refresh */
  dc_data *value; /* value returned by get. replaced on update or refresh */
  dc_data *old_value; /* value storeed in old_value before updates */
  dc_data *initial_value; /* copied into value/old_value on reset */

  int nargs; /* number of args */
  list<dc_arg *> args; /* list of arguments replaced on call */
  
  dc_type rtype; /* return type -- see types.h */
  xtype xt;
  
  bool transparent; /* if true, this element will be excluded from sets */

  buf_info Buffer_Info; /* see huf_info.h */

  void set_frozen( bool set, tag inheritance_src );
  void set_dormant( bool set, bool inherited );

  /* true iff element has an evaluation function and either no args or all of 
     its args have default values */
  bool dynamic;

protected:
  bool force_update( void );

public:
  dc_element( void );
  /*dc_element( dc_func &f, cstring label, dc_component *parent = nil );*/
  dc_element( cstring label, dc_component *parent = nil );
  ~dc_element();

  /* inherited functions */
  const dc_type type( void ) const { return Element_t; }
  string type_string( void ) const { return string( "element" ); }

  bool update( void );
  bool refresh( void );
  dc_data *call( const f_list &args );
  
  /* updates value if necessary.  returns nil if no value or update error */
  dc_data *get( void );

  /* returns value held before last update */
  dc_data *get_previous( void ) const ; 

  /* checks status, but does not type check.  d must be temporary, and will be
     deleted by this */
  virtual bool set( dc_data &d );
  bool set_func( dc_func &f ); /* overrides inital value */
  void unset_func( void ); /* sets function to nil. error if no initial value */
  void set_type( const dc_type T ) { rtype = T; }
  bool is_transparent( void ) const { return transparent; }
  void set_transparent( const bool I ) { transparent = I; }

  dc_type get_rtype( void ) const { return rtype; }
  void get_xtype( xtype &T ) const { T = xt; }

  /* sets both value and old_value to d */
  bool init( dc_data &d, bool set_back = false );

  void add_arg( dc_arg &a );

  dc_arg *match( cstring ) const;

  /* returns rank of arg in arg_list or -1 if not present */
  int arg_rank( cstring ) const;
  bool is_arg( cstring ) const;
  int get_nargs( void ) const { return nargs; }
  dc_type get_arg_type( int rank ) const;

  ostream &display( ostream &stream = cout );
  friend ostream &operator<<( ostream &stream, dc_element &E ) {
    return E.display( stream );
  }

  ostream &display_fn( ostream &stream = cout ) const;

  /* returns true if at least one dependency was removed.
     assumes tags are sorted */
  bool remove_deps( const int, const tag[] );
  virtual int rehash( dep_list &dl );
  int rehash( void ) { dep_list dl; int n = rehash(dl); dl.clear(); return n; }
  void reset( void );

  bool is_simple( void ) const { return ( eval == nil ); }
  bool is_inited( void ) const { return ( initial_value != nil ); }
  bool is_dynamic( void ) const { return dynamic; }

  buf_info &buffer_info( void ) { return Buffer_Info; }

  void set_frozen( bool set ) { set_frozen( set, tag_error ); }
  void set_dormant( bool set ) { set_dormant( set, false ); }

  dc_label *duplicate( dc_node *parent ) const;

  friend dc_component::set_frozen( bool, tag );
  friend dc_component::set_dormant( bool, bool );
  friend dc_component::child_init( dc_label * );
  friend class dc_clock;
};


/* args rehashed by position f( 1,2,3 ) or name f( "a" = 1, "c" = 3 )
   some may use default f( 1,,3 ) or f(1,2) */
struct arg_data {
  string label; /* "" for none */
  dc_func *f;

  arg_data( cstring lbl, dc_func &F ) { label = lbl, f = &F; }
  arg_data( dc_func &F ) { f = &F; }
  ~arg_data( void ) { if( f ) delete( f ); }
};

/* - dc_func_call is a descendant od dc_func
   - dc_func_calls are used in functions to call an element with a list of
   arguments.
   NOTE -- any or all of dc_func_call's args may be nil unlike dc_op or 
   dc_set_op in which case the args default value is used */
class dc_func_call : public dc_func {
private:
  dc_element *E;
  int nargs;
  int max_rank;
  f_list args; /* may be gaps in this arg list with nil func *'s in some
		  elements. last element will always be non nil, and nargs is
		  total number of non-nil elements */

  /* used to rehash args */
  list<arg_data *> arg_data_list; /* may be nil */

  string hash_path; /* label that E was parsed from, used to rehash */
  dc_label *hash_origin; /* origin E was parsed from */

  /* used to rehash */
  bool add_arg( dc_func &, int n ); /* will place in nth spot, starting at 0 */
  
public:
  dc_func_call( cstring, dc_label * );
  virtual ~dc_func_call() { clear_args(); }

  dc_data *evaluate_f( void );

  void add_arg_data( dc_func &f ) { arg_data_list.append( new arg_data( f ) ); }
  void add_arg_data( cstring l, dc_func &f ) {
    arg_data_list.append( new arg_data( l, f ) ); }
  void add_nil_arg_data( void ) { arg_data_list.append( ( arg_data * )nil ); }
  
  void clear_args( void );

  int get_nargs( void ) const { return nargs; }
  dc_type get_rtype( void ) {
    if( E ) return E->get_rtype(); else return Undef_t; }
  void get_xtype( xtype & );

  void set_owner( dc_label *l )
    { owner = l; arg_data *ad; 
    forall( ad, arg_data_list ) { if( ad && ad->f ) ad->f->set_owner( l ); } }

  ostream &display( ostream &stream = cout ) const;

  dc_data *simplify( void );

  bool remove_deps( const int, const tag[] );

  int rehash( dep_list &, const tag );

  dc_func *duplicate( dc_label *, list<dc_arg *> * ) const;
};

inline ostream &operator<<( ostream &stream, const dc_arg &f ) {
  return f.display( stream );
}
inline ostream &operator<<( ostream &stream, const dc_func_call &f ) {
  return f.display( stream );
}

class dc_iter_loop : public dc_func {
protected:
  /* func is function called each iteration cycle.  it should contain references
     to arg, which is updated each iteration cycle. owned */
  dc_func *func;

  /* owned */
  dc_arg arg;

  /* set that supplies functions to plug into arg.  owned if label == "" */
  dc_set *set;
  
  /* list of args derived from set that is substituted into arg */
  f_list args;
  

  /* label and search_origin used to reset element or data set reference */
  string label; /* if label == "" then not rehashable */
  dc_label *search_origin;
  /* id is rehashed from set. element or data from which set is derived */
  dc_label *id;

  /* takes set and produces args */
  void hash_set( void );
public:
  dc_iter_loop( void );
  ~dc_iter_loop( void );
  
  void set_func( dc_func &F ) { if( func ) delete( func ); func = &F; }

  /* set_set fns require a rehash before changes take place */
  void set_set( cstring lbl, dc_label *s_o );
  void set_set( dc_set &S );
  
  void set_owner( dc_label *l );
  
  dc_arg &get_arg( void ) { return arg; }

  /* must be set once before first rehash */
  void init_arg( cstring N, dc_type T ) { arg.init( N, T ); }

  dc_data *evaluate_f( void );

  dc_type get_rtype( void ) { return func ? func->get_rtype() : Undef_t; }
 
  dc_data *simplify( void ) { return func ? func->simplify() : nil; }

  ostream &display( ostream &stream = cout ) const;
  bool remove_deps( const int n, const tag tags[] ) {
    return func ? func->remove_deps( n, tags ) : false; }

  int rehash( dep_list &, const tag );

  dc_func *duplicate( dc_label *, list<dc_arg *> * ) const;
};

#endif
