/* dev.c  94.12.09
 * Copyright 1983-1992   Albert Davis
 * top level device functions that call the specific one and
 * standard device functions to be used where nothing else is needed
 */
#include "ecah.h"
#include "branch.h"
#include "error.h"
#include "nodestat.h"
#include "options.h"
#include "probh.h"
#include "status.h"
#include "declare.h"
/*--------------------------------------------------------------------------*/
	branch_t* create_branch(const functions_t*);
	branch_t* create_std(const functions_t*);
	branch_t* copy_branch(const branch_t*);
	branch_t* copy_std(const branch_t*);
static	generic_t*  create_extra_stuff(const generic_t*);
	void	parse_branch(branch_t*,const char*,int*);
	void	  parse_std(branch_t*,const char*,int*);
	void	    parselabel(branch_t*,const char*,int*);
	void	      parselabel_raw(char*,const char*,int*);
	void	print_branch(const branch_t*,int,int);
	void	  print_std(const branch_t*,int,int);
	char*	    printlabel(const branch_t*,int);
	void	expand_branch(branch_t*);
	void	  expandgeneric(branch_t*,const branch_t*);
	double	probe_branch(const branch_t*,const char*);
	double	  probe_std(const branch_t*,const char*);
	double	  tr_probe_std(const branch_t*,const char*);
	double	  ac_probe_std(const branch_t*,const char*);
	xprobe_t xprobe_std(const branch_t*,const char*);
	int	dotr_branch(branch_t*);
	void	untr_branch(branch_t*);
	void	doac_branch(branch_t*);
	double	tr_review_branch(branch_t*);
/*--------------------------------------------------------------------------*/
extern       struct nodestuff ns;
extern const struct options opt;
extern const struct status stats;
extern const int inc_mode;
extern const int sim_mode;
extern const char e_om[], e_int[];
/*--------------------------------------------------------------------------*/
branch_t *create_branch(const functions_t *func)
{
 if (func && func->create){
    return (*(func->create))(func);
 }else{
    error(bERROR, e_int, "create_branch");
    return (branch_t*)NULL;
 }
}
/*--------------------------------------------------------------------------*/
/* create_std: create an empty branch structure, or copy a prototype
 * returns pointer to it.
 */
branch_t *create_std(const functions_t *func)
{
 branch_t *brh;
 
 if (func){
    int ii;
    brh = (branch_t*)calloc(1, func->elementsize);
    if (!brh)
       error(bERROR, e_om, "create_std");
    for (ii = 0;  ii <= NODESPERBRANCH;  ii++)
       brh->nodes[ii].e = brh->nodes[ii].t = brh->nodes[ii].m = INVALIDNODE;
    brh->f = func;
    brh->ssize = func->elementsize;
    if (func->x)
       brh->x = create_extra_stuff(func->x);
    brh->n = brh->nodes;
    brh->subckt = (branch_t*)NULL;
    brh->prev = brh->next = (branch_t*)NULL;
    brh->stprev = brh->stnext = (branch_t*)NULL;
    brh->parent = (branch_t*)NULL;
 }else{
    error(bERROR, e_int, "create_std");
    brh = (branch_t*)NULL;
 }
 return brh;
}
/*--------------------------------------------------------------------------*/
branch_t *copy_branch(const branch_t *brh)
{
 if (exists(brh) && brh->f->create){
    return (*(brh->f->copy))(brh);
 }else{
    error(bERROR, e_int, "copy_branch");
    return (branch_t*)NULL;
 }
}
/*--------------------------------------------------------------------------*/
/* copy_std: create an empty branch structure, or copy a prototype
 * returns pointer to it.
 */
branch_t *copy_std(const branch_t *proto)
{
 branch_t *brh;
 
 if (proto){
    brh = (branch_t*)calloc(1,proto->ssize);
    if (!brh)
       error(bERROR, e_om, "copy_std");
    (void)memcpy((void*)brh, (const void*)proto, proto->ssize);
    if (brh->x)
       brh->x = create_extra_stuff(brh->x);
    brh->n = brh->nodes;
    brh->subckt = (branch_t*)NULL;
    brh->prev = brh->next = (branch_t*)NULL;
    brh->stprev = brh->stnext = (branch_t*)NULL;
    brh->parent = (branch_t*)NULL;
 }else{
    error(bERROR, e_int, "copy_std");
    brh = (branch_t*)NULL;
 }
 return brh;
}
/*--------------------------------------------------------------------------*/
/* create_extra_stuff: copy the extra info for special parts (mosfets, etc)
 */
static generic_t *create_extra_stuff(const generic_t *proto)
{
 if (proto){
    generic_t *x;
    x = (generic_t*)calloc(1,proto->ssize);
    if (!x)
       error(bERROR, e_om, "extra_stuff");
    (void)memcpy((void*)x, (const void*)proto, proto->ssize);
    if (x->x)
       x->x = create_extra_stuff(x->x);
    return x;
 }else{
    error(bERROR, e_int, "create_extra_stuff");
    return (generic_t*)NULL;
 }
}
/*--------------------------------------------------------------------------*/
void parse_branch(branch_t *brh, const char *cmd, int *cnt)
{
 if (exists(brh) && brh->f->parse){
    ((*(brh->f->parse))(brh,cmd,cnt));
 }else{
    error(bERROR, e_int, "parse_branch");
 }
}
/*--------------------------------------------------------------------------*/
/* parse_std: parse most ordinary elements (resistor, vccs, cap, etc.)
 */
void parse_std(branch_t *brh, const char *cmd, int *cnt)
{
 parselabel(brh,cmd,cnt);
 (void)parsenodes(brh,cmd,cnt);
 parseexpr(brh,cmd,cnt);
 if (brh->f->super)
    brh->f = brh->f->super;
}
/*--------------------------------------------------------------------------*/
/* parselabel: parse element label from input string
 * result in brh.
 */
void parselabel(branch_t *brh, const char *cmd, int *cnt)
{
 parselabel_raw(brh->label, cmd, cnt);
}
/*--------------------------------------------------------------------------*/
/* parselabel: parse element label from input string
 * result in brh.
 */
void parselabel_raw(char *label, const char *cmd, int *cnt)
{
 (void)ctostr(cmd, cnt, label, LABELEN, TOKENTERM);
 label[0] = to_upper(label[0]);
}
/*--------------------------------------------------------------------------*/
void print_branch(const branch_t *brh, int where, int detail)
{
 if (exists(brh) && brh->f->print){
    (*(brh->f->print))(brh,where,detail);
 }else{
    error(bERROR, e_int, "print_branch");
 }
}
/*--------------------------------------------------------------------------*/
void print_std(const branch_t *brh, int where, int detail)
{
 (void)printlabel(brh,where);
 printnodes(brh,where);
 printexpr(brh,where);
 mputc('\n', where);
}
/*--------------------------------------------------------------------------*/
char* printlabel(const branch_t *brh, int where)
{
 static char label[BUFLEN];/* CAUTION: static area overwritten every call */

 strcpy(label, brh->label);
 while (brh = brh->parent, exists(brh)){
    strcat(label, ".");
    strncat(label, brh->label, BUFLEN-strlen(label)-2);
 }
 strcpy(&(label[BUFLEN-3]), "++");
 if (where)
    mprintf(where, "%s  ", label);
 return label;
}
/*--------------------------------------------------------------------------*/
void expand_branch(branch_t *brh)
{
 if (exists(brh)){
    switch (brh->method_u){
    case mGEAR:
    case mTRAPEZOID:
       brh->method_a = brh->method_u;
       break;
    case mSTIFF:
       brh->method_a = mGEAR;
       break;
    default:
       brh->method_a = opt.method;
       break;
    }
    if (brh->f->expand){
       (*(brh->f->expand))(brh);
    }
    /* BUG: (maybe) this function is called by expand_list in allocate,
     * which calls itself recursively to expand subckts.
     */
 }
}
/*--------------------------------------------------------------------------*/
void expandgeneric(branch_t *brh, const branch_t *modellist)
{
 if (!brh->subckt){
    error(bTRACE, "%s: expanding\n", printlabel(brh,NO));
 }else{
    error(bTRACE, "%s: re-expanding\n", printlabel(brh,NO));
    brh->subckt = (branch_t*)NULL;  /* detach subckt */
 }

 if (brh->x){
    if (!brh->x->m){
       const branch_t *model;
       model = findbranch_sametype(brh->x->modelname, modellist);
       if (!model){
	  error(bERROR, "%s: can't find model: %s\n",
			printlabel(brh,NO),   brh->x->modelname);
       }
       brh->x->m = model->x;
    }
 }
}
/*--------------------------------------------------------------------------*/
double probe_branch(const branch_t *brh, const char *what)
{
 if (exists(brh) && brh->f->probe){
    return (*(brh->f->probe))(brh,what);
 }else{					/* return 0 if doesn't exist */
    return 0.0;				/* happens when optimized models */
 }					/* don't have all parts */
}
/*--------------------------------------------------------------------------*/
double probe_std(const branch_t *brh, const char *what)
{
 if (sim_mode == sAC){
    return ac_probe_std(brh,what);
 }else{
    return tr_probe_std(brh,what);
 }
}
/*--------------------------------------------------------------------------*/
double tr_probe_std(const branch_t *brh, const char *what)
{
 double involts;
 double outvolts;
 int dummy = 0;

 outvolts = tr_volts(&(brh->n[OUT1]),&(brh->n[OUT2]));
 if (brh->f->devclass == rnTWOPORT){
    involts = tr_volts(&(brh->n[IN1]),&(brh->n[IN2]));
 }else if (brh->f->devclass == rnONEPORT || brh->f->devclass == rnNISOURCE){
    involts = outvolts;
 }else if (brh->f->devclass == rnSOURCE){
    involts  = 0.;
 }else{
    involts  = 0.;
    error(bERROR, e_int, "trprobe_std");
 }

 setmatch(what,&dummy);
 if (rematch("Vo")){
    return outvolts;
 }else if (rematch("VI")){
    return involts;
 }else if (rematch("I")){
    return brh->m0.f1 * involts + brh->m0.c0;
 }else if (rematch("P")){
    return (brh->m0.f1 * involts + brh->m0.c0) * outvolts;
 }else if (rematch("PD")){
    double p = (brh->m0.f1 * involts + brh->m0.c0) * outvolts;
    return (p > 0.) ? p : 0.;
 }else if (rematch("PS")){
    double p = (brh->m0.f1 * involts + brh->m0.c0) * outvolts;
    return (p < 0.) ? -p : 0.;
 }else if (rematch("INput")){
    return brh->y0.x;
 }else if (rematch("F")){
    return (brh->y0.f0 != LINEAR) ? brh->y0.f0 : (brh->y0.x * brh->y0.f1);
 }else if (rematch("NV")){
    return brh->val;
 }else if (rematch("EV")){
    return brh->y0.f1;
 }else if (rematch("Y")){
    return brh->m0.f1;
 }else if (rematch("EIV")){
    return brh->m0.x;
 }else if (rematch("IOFfset")){
    return brh->m0.c0;
 }else if (rematch("IPassive")){
    return brh->m0.f1 * involts;
 }else if (rematch("DIdt")){
    double i0  = (brh->m0.f1  * brh->m0.x  + brh->m0.c0);
    double it1 = (brh->mt1.f1 * brh->mt1.x + brh->mt1.c0);
    return  (i0 - it1) / (brh->time0 - brh->time1);
 }else if (rematch("DTNew")){
    return brh->timef - brh->time0;
 }else if (rematch("DTOld")){
    return brh->time0 - brh->time1;
 }else if (rematch("TIMEF")){
    return brh->timef;
 }else if (rematch("TIME")){
    return brh->time0;
 }else if (rematch("TIMEO")){
    return brh->time1;
 }else if (rematch("R")){
    return (brh->m0.f1!=0.) ? 1/brh->m0.f1 : MAXDOUBLE;
 }else if (rematch("Z")){
    return trz(brh->n[OUT1],brh->n[OUT2],brh->m0.f1);
 }else{ /* bad parameter */
    return NOT_VALID;
 }
}
/*--------------------------------------------------------------------------*/
double ac_probe_std(const branch_t *brh, const char *what)
{
 int length;
 mod_t modifier = mtNONE;
 int want_db = NO;
 char parameter[LABELEN+1];
 xprobe_t xp;
 
 strcpy(parameter, what);
 length = strlen(what);
 if (length > 2  &&  match(&parameter[length-2], "DB")){
    want_db = YES;
    length -= 2;
 }
 if (length > 1){
    switch (to_lower(parameter[length-1])){
       case 'm': modifier = mtMAG;   length--;	break;
       case 'p': modifier = mtPHASE; length--;	break;
       case 'r': modifier = mtREAL;  length--;	break;
       case 'i': modifier = mtIMAG;  length--;	break;
       default:  modifier = mtNONE;		break;
    }
 }
 parameter[length] = '\0';
 
 xp = (*(brh->f->xprobe))(brh, parameter);
 if (!xp.ok){
    xp = (*(brh->f->xprobe))(brh, what);
 }
 if (!xp.ok){
    return NOT_VALID;
 }else{
    double rvalue = 0.;
    if (modifier == mtNONE)
       modifier = xp.modifier;
    switch (modifier){
       case mtMAG:	rvalue = CABS(xp.value);		break;
       case mtPHASE:	rvalue = phase(xp.value.x,xp.value.y);	break;
       case mtREAL:	rvalue = xp.value.x;			break;
       case mtIMAG:	rvalue = xp.value.y;			break;
       default:		error(bERROR, e_int, "acprobe_std");	break;
    }
    if (want_db){
       return xp.dbscale * log10(MAX(rvalue,VOLTMIN));
    }
    return rvalue;
 }
}
/*--------------------------------------------------------------------------*/
xprobe_t xprobe_std(const branch_t *brh, const char *what)
{
 complex_t involts;
 complex_t outvolts;
 complex_t admittance = brh->acg;
 complex_t source;
 int dummy = 0;
 xprobe_t result = {{0.,0.}, mtMAG, 20., YES};
 
 outvolts.x = ns.acx[brh->n[OUT1].m] - ns.acx[brh->n[OUT2].m];
 outvolts.y = ns.acy[brh->n[OUT1].m] - ns.acy[brh->n[OUT2].m];
 if (brh->f->devclass == rnTWOPORT){
    involts.x = ns.acx[brh->n[IN1].m] - ns.acx[brh->n[IN2].m];
    involts.y = ns.acy[brh->n[IN1].m] - ns.acy[brh->n[IN2].m];
    source.x = source.y = 0.;
 }else if (brh->f->devclass == rnONEPORT){
    involts = outvolts;
    source.x = source.y = 0.;
 }else if (brh->f->devclass == rnNISOURCE){
    involts = outvolts;
    source.x = -brh->ev.x / opt.shortckt;
    source.y = -brh->ev.y / opt.shortckt;
 }else if (brh->f->devclass == rnSOURCE){
    involts.x = 1.;
    involts.y = 0.;
    source = brh->ev;
 }else{
    involts.x = 1.;
    involts.y = 0.;
    source.x = source.y = 0.;
    error(bERROR, e_int, "xprobe_std");
 }

 setmatch(what,&dummy);
 if (rematch("V")){				/* volts */
    result.value = outvolts;
 }else if (rematch("I")){			/* amps */
    result.value = cadd(cmul(involts,admittance),source);
 }else if (rematch("P")){			/* complex "power" */
    result.value = ccmul(outvolts,cadd(cmul(involts,admittance),source));
    result.dbscale = 10.;
    result.modifier = mtREAL;
 }else if (rematch("NV")){			/* nominal value */
    result.value.x = brh->val;
 }else if (rematch("EV")){			/* effective value */
    result.value = brh->ev;
 }else if (rematch("Y")){			/* admittance */
    result.value = admittance;
    result.modifier = mtREAL;
 }else if (rematch("R")){			/* complex "resistance" */
    if (admittance.x==0. && admittance.y==0.){
       complex_t r = {MAXDOUBLE, 0.};
       result.value = r;
    }else{
       result.value = cflip(admittance);
    }
 }else if (rematch("Z")){			/* port impedance */
    result.value = acz(brh->n[OUT1],brh->n[OUT2],admittance);

 }else{ /* bad parameter */
    complex_t bad = {NOT_VALID, NOT_VALID};
    result.value = bad;
    result.ok = NO;
 }
 return result;
}
/*--------------------------------------------------------------------------*/
int dotr_branch(branch_t *brh)
{
 if (exists(brh) && brh->f->dotr){
    if (brh->evaliter == stats.iter[iTOTAL]){
       error(bDANGER, e_int, "double load");
       return brh->converged;
    }else{
       const functions_t *f;
       f = brh->f;
       if (f->refnode == rnCOMP){
	  brh->bypass = brh->parent->bypass;
       }
       brh->evaliter = stats.iter[iTOTAL];
       trsetup(brh);
       return ((*(brh->f->dotr))(brh));
    }
 }else{
    return YES;
 }
}
/*--------------------------------------------------------------------------*/
void untr_branch(branch_t *brh)
{
 if (exists(brh) && brh->f->untr){
    ((*(brh->f->untr))(brh));
 }
}
/*--------------------------------------------------------------------------*/
void doac_branch(branch_t *brh)
{
 if (exists(brh) && brh->f->doac){
    if (brh->evaliter != stats.iter[iTOTAL]){
       brh->evaliter = stats.iter[iTOTAL];
       ((*(brh->f->doac))(brh));
    }		/* BUG: "double load" can occur when a current controlled   */
 }		/* source has a forward reference.  See cccs and ccvs.	    */
}
/*--------------------------------------------------------------------------*/
double tr_review_branch(branch_t *brh)
{
 if (exists(brh) && brh->f->tr_review){
    return brh->timef = ((*(brh->f->tr_review))(brh));
 }else{
    return brh->timef = BIGBIG;
 }
}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
