/* agent_circuit.h */

//#define COMPILE_AGENT_CIRCUIT

#ifdef COMPILE_AGENT_CIRCUIT

#ifndef AGENT_CIRCUIT__H
#define AGENT_CIRCUIT__H

#include "rmatrix.h"
#include "vector.h"
#include "user_types.h"
#include "root.h"
#include "tag.h"
#include "real.h"


/* element in this context refers to a branch element which is implemented with
   a synergy componment.
   synergy elements are used to fill out the info in each branch element */
 */

class dc_pipe;

struct LinkStruct
{
  Eag_branch::plus_type plus, destPlus; 
    /* PPlus if this is from +ve terminal, Pminus if -ve */
};

class Eag_branch {
 public:
  Eag_branch(tag tid);
  ~Eag_branch(void);

  bool addLink(tag tid, plus_type plus, tag t, plus_type tPlus, Eag_branch *tbranch, 
	       bool *newNode); 
  bool delLink(tag tid, tag t);
  bool getProperties(tag tid, double *Y, double *Ig);
  bool set(tag tid, double dV, double I, double Qheat);
  bool get(tag tid, double *dV, double *I, double *Qheat);
  int  node(plus_type plus); // returns node no corresponding to "plus"

  enum plus_type {
    Pminus = 0,
    Pplus,
    NPtype}

 protected:
  tag id;
  int NodeNo[NPtype]; /* Node numbers >= 0 this branch is attached to, 
			 -1 if invalid or unspecified */
  dc_agent_circuit::circ_type branch;
  dc_data *info[dc_agent_circuit::NCInfo];
  double dV, I, Qheat; /* this is what the agent calculates */
  dictionary <tag, LinkStruct *> links;

 private:
  bool badTag(tag tid);
  void tagError(string &s, tag tid);
};

class dc_agent_circuit {
public:
  enum info_type { /* Add, Vc for battery */
    CV_Undef = -1,
    dV, = 0,
    I,
    Qheat,
    Res,
    Cap,
    Ind, 
    dVMax, 
    dVMin,
    IMax,
    IMin,
    VZn, /* zener voltage */
    Spd, /* motor speed in rad/s */
    kI,
    kV,
    On, /* boolean -- switch is on */
    Y,
    Ig, /* source current */
    Nu, /* efficiency */
    T,
    TMax,
    TMin,
    NCInfo
  };
  
  /* pipe names recognized by parser */
  static const char *const 
  info_name[NCInfo] = { "e_dv", "e_i", "e_q", "e_r", "e_c", "e_l", 
			"e_dvmax", "e_dvmin", "e_imax", "e_imin", "e_vzn",
			"e_spd", "e_ki", "e_kv", "e_on", "e_y", "e_ig",
			"e_nu", "e_t", "e_tmax", "e_tmin" };
  
  static const dc_type info_dtype[NCInfo] = { Real_t, Real_t, Real_t, Real_t,
					      Real_t, Real_t, Real_t, Real_t,
					      Real_t, Real_t, Real_t, Real_t,
					      Real_t, Real_t, Boolean_t, Real_t,
					      Real_t, Real_t. Real_t, Real_t,
					      Real_t, Real_t };

  /* "Resistor", "Capacitor", "Inductor", "Transistor", "Amplifier", 
     "Motor", "Switch", "dcdc", "battery", "Custom" */
  enum circ_type {
    CT_Undef = 0,
    Resistor = 1,
    Capacitor,
    Inductor,
    Transistor,
    Amp,
    Motor,
    Switch,
    Dcdc,
    Battery,
    Custom,
    NCTypes
  };

  static const char *const 
  circ_type_name[NCTypes] = { "Undef", "resistor", "capacitor", "inductor", 
			      "transistor", "amplifier", "motor", "switch", 
			      "dcdc", "battery", "custom" };


protected:
  int NNodes;
  dc_rect_matrix *A_mx, *Y_mx;
  dc_vector *Is_vec, *Vn_vec, *Vb_vec;
  dictionary<tag, Eag_branch *> Ebranch; // Dictionary of E agent branch elements
    double minDt, minDv, minVbfrac, maxIter;

  static tag_Gnd = 0; // OK?

  /* agent_type must be present in all electrical components
     ex.
     component r1 {
       type resistor;
       e_dV = <nil>;
       e_I = 25 mA;
       e_R = 10 kiloohm;
     }

     link r1, battery : electrical( +, - );
     */

public:
  dc_agent_circuit( void );
  ~dc_agent_circuit( void );
  
//  bool set_ground_name( cstring name = "" );

  /* don't have to, can always be referred to as ground */
  bool set_connection( tag src, bool, tag tgt, bool ); // gives info to agent
  /* if branch tag(s) do not exist, instatiate
     call addLinks in both the branches being connected */

  dc_data *get_element( tag branch, info_type ) const; // used to query agent
  
  // set termination criteria for non-linear conv.
  bool set_termination_criteria( double dV = 1.E-4, double Vbfrac = 0.001, 
				 int max_iter = 10);

  bool set_min_dt_criteria( double t = 0.001);
  // iterate a non-linear & halve dt, until dt < t min

  bool model_gen( void ); // all elements & links set, generate model
  bool model_sim( dc_real *dt ); // simulate model for dt
  bool model_reset( void ); // destroy existing information, prepare for new model

  friend dc_root;
};

dc_agent_circuit::circ_type query_branch( tag branch, dc_data *info[dc_agent_circuit::NCInfo] );

/* indexed to enum type, info_type, returns nil if undefined, value otherwise */

extern dc_agent_circuit circuit_agent;

/* Agent should not use the below stuff */
/* functions referred to in table in pipe_iface.h */
extern inline bool     is_circuit_pfn( dc_component * );
extern inline bool     cir_set_dV( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_dV( dc_pipe &p );
extern inline bool     cir_set_I( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_I( dc_pipe &p );
extern inline bool     cir_set_Q( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_Q( dc_pipe &p );
extern inline bool     cir_set_R( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_R( dc_pipe &p );
extern inline bool     cir_set_C( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_C( dc_pipe &p );
extern inline bool     cir_set_L ( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_L ( dc_pipe &p );
extern inline bool     cir_set_dVMax( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_dVMax( dc_pipe &p );
extern inline bool     cir_set_dVMin( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_dVMin( dc_pipe &p );
extern inline bool     cir_set_IMax( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_IMax( dc_pipe &p );
extern inline bool     cir_set_IMin( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_IMin( dc_pipe &p );
extern inline bool     cir_set_VZn( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_VZn( dc_pipe &p );
extern inline bool     cir_set_Spd( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_Spd( dc_pipe &p );
extern inline bool     cir_set_kI( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_kI( dc_pipe &p );
extern inline bool     cir_set_kV( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_kV( dc_pipe &p );
extern inline bool     cir_set_On( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_On( dc_pipe &p );
extern inline bool     cir_set_Y( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_Y( dc_pipe &p );
extern inline bool     cir_set_Ig( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_Ig( dc_pipe &p );
extern inline bool     cir_set_Nu( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_Nu( dc_pipe &p );
extern inline bool     cir_set_T( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_T( dc_pipe &p );
extern inline bool     cir_set_TMax( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_TMax( dc_pipe &p );
extern inline bool     cir_set_TMin( dc_pipe &p, dc_data &v );
extern inline dc_data *cir_get_TMin( dc_pipe &p );

#endif // end ifndef AGENT_CIRCUIT__H

#endif // end ifdef COMPILE_AGENT_CIRCUIT
