/* d_subckt.c  94.12.09
 * Copyright 1983-1992   Albert Davis
 * subcircuit stuff
 * base class for other elements using internal subckts
 * netlist syntax:
 * device: Xxxxx <nodelist> <subckt-name>
 * model:  .subckt <subckt-name> <nodelist>
 *	   (device cards)
 *	   .ends <subckt-name>
 */
#include "ecah.h"
#include "branch.h"
#include "d_subckt.h"
#include "error.h"
#include "mode.h"
#include "types.h"
#include "declare.h"
/*--------------------------------------------------------------------------*/
	void	cmd_ends(const char*,int*);
static  branch_t  *create_subckt(const functions_t*);
static  branch_t  *copy_subckt(const branch_t*);
static  void    parse_subckt(branch_t*,const char*,int*);
static  void    print_subckt(const branch_t*,int,int);
static  branch_t  *create_model_subckt(const functions_t*);
static  branch_t  *copy_model_subckt(const branch_t*);
static  void    parse_model_subckt(branch_t*,const char*,int*);
static  void    print_model_subckt(const branch_t*,int,int);
static  void    expand_subckt(branch_t*);
	void    expandsubckt(branch_t*,const char*);
static	double	probe_subckt(const branch_t*,const char*);
	int     tr_subckt(branch_t*);
	void    un_subckt(branch_t*);
	void    ac_subckt(branch_t*);
	double	tr_review_subckt(branch_t*);
/*--------------------------------------------------------------------------*/
static struct subckt defalt = {(generic_t*)NULL, sizeof(struct subckt), 
   (generic_t*)NULL, sDEFAULT_modelname, /*more*/};
static struct smod defaltmodel = {(generic_t*)NULL, sizeof(struct smod), 
   (generic_t*)NULL, sDEFAULT_modelname, /*more*/};
static branch_t modellist = {(generic_t*)NULL, sizeof(branch_t), 
   &model_subckt, &modellist, &modellist, &modellist, &modellist, 
   (branch_t*)NULL, (branch_t*)NULL, sDEFAULT_modelname, /*more*/};
static branch_t *(neststack[RECURSE]);
static char namestack[LABELEN+1][RECURSE];
static int nestlevel;
extern branch_t *insertbefore;
extern const char e_int[];
/*--------------------------------------------------------------------------*/
functions_t dev_subckt = {
   (generic_t*)&defalt,	/* x */
   sizeof(functions_t),	/* ssize */
   sizeof(branch_t),	/* elementsize */
   (functions_t*)NULL,	/* super */
   PORTSPERSUBCKT, 	/* numnodes */
   rnALL,		/* refnode */
   YES,			/* isdevice */
   create_subckt,	/* create */
   copy_subckt,		/* copy */
   parse_subckt,	/* parse */
   print_subckt,	/* print */
   expand_subckt,	/* expand */
   probe_subckt,	/* probe */
   NULL,		/* tr_probe */
   NULL,		/* ac_probe */
   NULL,		/* xprobe */
   tr_subckt,		/* dotr */
   un_subckt,		/* untr */
   ac_subckt,		/* doac */
   NULL,		/* trfun1 */
   NULL,		/* trfun0 */
   NULL,		/* acfun */
   NULL,		/* tr_guess */
   NULL,		/* tr_advance */
   tr_review_subckt	/* tr_review */
};
functions_t model_subckt = {
   (generic_t*)&defaltmodel,	/* x */
   sizeof(functions_t),	/* ssize */
   sizeof(branch_t),	/* elementsize */
   (functions_t*)NULL,	/* super */
   PORTSPERSUBCKT, 	/* numnodes */
   rnMODEL,		/* refnode */
   NO,			/* isdevice */
   create_model_subckt,	/* create */
   copy_model_subckt,	/* copy */
   parse_model_subckt,	/* parse */
   print_model_subckt,	/* print */
   NULL,		/* expand */
   NULL,		/* probe */
   NULL,		/* tr_probe */
   NULL,		/* ac_probe */
   NULL,		/* xprobe */
   NULL,		/* dotr */
   NULL,		/* untr */
   NULL,		/* doac */
   NULL,		/* trfun1 */
   NULL,		/* trfun0 */
   NULL,		/* acfun */
   NULL,		/* tr_guess */
   NULL,		/* tr_advance */
   NULL			/* tr_review */
};
/*--------------------------------------------------------------------------*/
void cmd_ends(const char* cmd, int *cnt)
{
 if (nestlevel == 0)
    error(bWARNING, "ends not in subckt\n");
 else
    nestlevel--;

 if (cmd[*cnt]){
    if (!pmatch(cmd, cnt, namestack[nestlevel]))
       error(bERROR, "ends tag [%s] does not match subckt [%s]\n",
          &cmd[*cnt], namestack[nestlevel]);
 }else{
    nestlevel = 0;
 }
 insertbefore = neststack[nestlevel];
}
/*--------------------------------------------------------------------------*/
static branch_t *create_subckt(const functions_t *func)
{
 branch_t *brh;
 struct subckt *x;

 brh = create_std(func);
 x = (struct subckt*)brh->x;
 brh->n = x->n;
 return brh;
}
/*--------------------------------------------------------------------------*/
static branch_t *copy_subckt(const branch_t *proto)
{
 branch_t *brh;
 struct subckt *x;

 brh = copy_std(proto);
 x = (struct subckt*)brh->x;
 brh->n = x->n;
 return brh;
}
/*--------------------------------------------------------------------------*/
static void parse_subckt(branch_t *brh, const char *cmd, int *cnt)
{
 struct subckt *x;
 x = (struct subckt*)brh->x;

 parselabel(brh,cmd,cnt);
 (void)parsenodes(brh,cmd,cnt);
 (void)ctostr(cmd, cnt, x->modelname, LABELEN, TOKENTERM);
}
/*--------------------------------------------------------------------------*/
static void print_subckt(const branch_t *brh, int where, int detail)
{
 struct subckt *x;
 x = (struct subckt*)brh->x;
 
 (void)printlabel(brh,where);
 printnodes(brh,where);
 mprintf(where, " %s\n",    x->modelname);
}
/*--------------------------------------------------------------------------*/
static branch_t *create_model_subckt(const functions_t *func)
{
 branch_t *brh;
 struct smod *x;

 brh = create_std(func);
 x = (struct smod*)brh->x;
 brh->n = x->n;
 brh->stprev = &modellist;
 return brh;
}
/*--------------------------------------------------------------------------*/
static branch_t *copy_model_subckt(const branch_t *proto)
{
 branch_t *brh;
 struct smod *x;

 brh = copy_std(proto);
 x = (struct smod*)brh->x;
 brh->n = x->n;
 brh->stprev = &modellist;
 return brh;
}
/*--------------------------------------------------------------------------*/
static void parse_model_subckt(branch_t *brh, const char *cmd, int *cnt)
{
 struct smod *m;
 m = (struct smod*)brh->x;

 if (nestlevel >= RECURSE)
    error(bERROR,"%s: subckt nesting too deep\n", printlabel(brh,NO));

 (void)ctostr(cmd, cnt, brh->label, LABELEN, TOKENTERM);
 (void)parsenodes(brh,cmd,cnt);

 strcpy(namestack[nestlevel], brh->label);
 neststack[nestlevel] = insertbefore;
 nestlevel++;
 brh->subckt = insertbefore = insertbranch(create_branch(&dev_comment));
 m->x = (generic_t*)NULL;
}
/*--------------------------------------------------------------------------*/
static void print_model_subckt(const branch_t *brh, int where, int detail)
{
 branch_t *x, *stop;

 mprintf(where, ".subckt %s ", brh->label);
 printnodes(brh,where);
 mprintf(where, "\n");

 x = stop = brh->subckt;
 if (x){
    do {
       print_branch(x, where, NO);
    } while (x = x->next,  x != stop);
 }
 mprintf(where, "*+ends %s\n", brh->label);
}
/*--------------------------------------------------------------------------*/
static void expand_subckt(branch_t *brh)
{
 struct subckt *x;
 x = (struct subckt*)brh->x;
 expandsubckt(brh,x->modelname);
 if (!brh->subckt){
    error(bERROR, "");
 }
 brh->tracesubckt = YES;
}
/*--------------------------------------------------------------------------*/
void expandsubckt(branch_t *brh, const char *modelname)
{
 const branch_t *model;
 /*struct subckt *x;*/
 branch_t *scan;
 branch_t *stop;
 int port, i;
 int map[NODESPERSUBCKT];

 model = findbranch_sametype(modelname, &modellist);
 if (!model){
    error(bDANGER,"%s: can't find subckt: %s\n",printlabel(brh,NO),modelname);
    brh->subckt = (branch_t*)NULL;
    return;
 }

 for (i = 0;  i < NODESPERSUBCKT;  i++)	    /* initialize: all nodes unused */
    map[i] = UNUSED;

 stop = scan = nextbranch_dev(model->subckt);
 do {						 /* scan elements of subckt */
    int ii;						 /* mark nodes used */
    for (ii = 0;  scan->n[ii].e != INVALIDNODE;  ii++){
       if (scan->n[ii].e > NODESPERSUBCKT)
          error(bERROR, "%s: too many internal nodes\n", model->label);
       map[scan->n[ii].e] = USED;
    }
 } while (scan=nextbranch_dev(scan),  scan != stop);

 map[0] = 0;
 for (port = 0;  model->n[port].e != INVALIDNODE;  port++){    /* map ports */
    if (model->n[port].e > NODESPERSUBCKT)
       error(bERROR, "internal error: subckt node out of range: %s\n",
       			model->label);
    map[model->n[port].e] = brh->n[port].t;
 }

 for (i = 0;  i < NODESPERSUBCKT;  i++){
    if (map[i] == USED){
       map[i] = newnode_subckt();	 /* assign number to internal nodes */
    }
 }

 if (!brh->subckt){
    error(bTRACE, "%s: expanding\n", printlabel(brh,NO));
    stop = scan = nextbranch_dev(model->subckt);
    do {
       branch_t *scratch;				     /* copy subckt */
       scratch = copy_branch(scan);
       scratch->parent = brh;
       scratch->next = brh->subckt;
       brh->subckt = insertbranch(scratch);
    } while (scan=nextbranch_dev(scan),  scan != stop);
 }else{
    error(bTRACE, "%s: re-expanding\n", printlabel(brh,NO));
 }

 stop = scan = nextbranch_dev(brh->subckt);
 do {							     /* patch nodes */
    int ii;
    for (ii = 0;  scan->n[ii].e != INVALIDNODE;  ii++){
       if (scan->n[ii].e < 0)
	  error(bERROR, e_int, "bad node");
       if (map[scan->n[ii].e] < 0)
          error(bERROR, e_int, "node map");
       scan->n[ii].t = map[scan->n[ii].e];
    }
    scan->n[ii].t = INVALIDNODE;
 } while (scan=nextbranch_dev(scan),  scan != stop);
}
/*--------------------------------------------------------------------------*/
static double probe_subckt(const branch_t *brh, const char *what)
{
 node_t ground;
 int dummy = 0;

 ground.e = ground.m = ground.t = 0;
 if (!brh->subckt)
    error(bERROR, "internal error: %s not expanded\n", printlabel(brh,NO));

 setmatch(what,&dummy);
 if (rematch("V")){
    int nn = ctoi(what,&dummy);			/* BUG: no bounds check */
    return tr_volts(&(brh->n[nn+1]),&ground);
 }else if (rematch("P")){
    branch_t *pb, *stop;
    double power = 0.;
    stop = pb = brh->subckt;
    do {
       power += probe_branch(pb,"P");
    } while (pb=nextbranch_dev(pb), pb != stop);
    return power;
 }else if (rematch("PD")){
    branch_t *pb, *stop;
    double power = 0.;
    stop = pb = brh->subckt;
    do {
       power += probe_branch(pb,"PD");
    } while (pb=nextbranch_dev(pb), pb != stop);
    return power;
 }else if (rematch("PS")){
    branch_t *pb, *stop;
    double power = 0.;
    stop = pb = brh->subckt;
    do {
       power += probe_branch(pb,"PS");
    } while (pb=nextbranch_dev(pb), pb != stop);
    return power;
 }else{ /* bad parameter */
    return NOT_VALID;
 }
 /*NOTREACHED*/
}
/*--------------------------------------------------------------------------*/
int tr_subckt(branch_t *brh)
{
 return brh->converged = tr_fill_rl(brh->subckt);
}
/*--------------------------------------------------------------------------*/
void un_subckt(branch_t *brh)
{
 tr_unfill_rl(brh->subckt);
}
/*--------------------------------------------------------------------------*/
void ac_subckt(branch_t *brh)
{
 ac_fill_rl(brh->subckt);
}
/*--------------------------------------------------------------------------*/
double tr_review_subckt(branch_t *brh)
{
 return tr_review_rl(brh->subckt);
}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
