/* modify.c  94.11.20
 * Copyright 1983-1992   Albert Davis
 */
#include "ecah.h"
#include "branch.h"
#include "error.h"
#include "io.h"
#include "declare.h"
/*--------------------------------------------------------------------------*/
	void	cmd_modify(const char*,int*);
	void	cmd_fault(const char*,int*);
static	void	modify_fault(const char*,int*,int);
static	double	sweep_fix(const char*,int*,const branch_t*,double);
static	int	faultbranch(branch_t*,double);
	void	cmd_restore(void);
	void	cmd_unfault(void);
/*--------------------------------------------------------------------------*/
extern const struct ioctrl io;

extern const char e_int[], e_om[];
extern const int crtplot;
extern const int swp_type[];
extern const int swp_count[], swp_steps[];
extern const int swp_nest;

static branch_t *faultlist;
static unsigned gotsize = 0;
static unsigned fcount = 0;
#define MODIFY 1
#define FAULT 0
/*--------------------------------------------------------------------------*/
void cmd_modify(const char *cmd, int *cnt)
{
 modify_fault(cmd,cnt,MODIFY);
}
/*--------------------------------------------------------------------------*/
void cmd_fault(const char *cmd, int *cnt)
{
 modify_fault(cmd,cnt,FAULT);
}
/*--------------------------------------------------------------------------*/
static void modify_fault(const char *cmd, int *cnt, int command)
{
 branch_t *brh;
 int cc;		    /* character counter			    */
 int next = 0;		    /* remember char counter, for next arg	    */
 int count;		    /* count number of changes			    */
 double value;		    /* new value				    */

 dealloc(YES);
 for (	; isalpha(cmd[*cnt]) ; *cnt=next){
    count = 0;
    for (brh = firstbranch_all();
    	 cc= *cnt, brh = findbranch(cmd,&cc,brh,lastbranch_all()), exists(brh);
    /**/ brh = nextbranch_all(brh)){
       value = ctof(cmd,&cc);
       if (command == MODIFY){
          brh->val = value;
       }else{ /* command == FAULT */
          value = sweep_fix(cmd,&cc,brh,value);
          if (!faultbranch(brh,value))
	     error(bWARNING, e_int, "fault");
       }
       count++;
       next = cc;
       if (brh == lastbranch_all())
          break;
    }
    if (count == 0)
       break;
 }
 syntax_check(cmd,cnt,bWARNING);
}
/*--------------------------------------------------------------------------*/
/* sweep_fix: fix the value for sweep command.
 * if not sweeping, return value.
 */
static double sweep_fix(
	const char *cmd,
	int *cc, 
	const branch_t *brh, 
	double value)
{
 if (swp_steps[swp_nest] != 0   &&   isfloat(cmd[*cc])){
    double end, offset;

    end = ctof(cmd,cc);
    offset = (double)swp_count[swp_nest] / (double)swp_steps[swp_nest];
    if (swp_type[swp_nest]=='L'){
       if (value == 0.)
	  error(bERROR, "log sweep can't pass zero\n");
       else
	  value *= pow( (end/value), offset );
    }else{
       value += (end-value) * offset;
    }

    if (!crtplot)
       mprintf( io.mstdout, "%u> sweep %s =%s\n",
	    swp_count[swp_nest]+1,
	    printlabel(brh,NO),
	    ftos(value,"             ",7,0)
	    );
 }
 return value;
}
/*--------------------------------------------------------------------------*/
/* faultbranch: "fault" a single branch. (temporarily change a value)
 * save the existing branch in "faultlist"
 * put the modified branch in the parts list
 * does not fix extensions here, up to caller
 * needs rework for pointers, extensions, etc.
 * unreliable.  does not maintain list consistency
 */
static int faultbranch(branch_t *brh, double value)
{
 if (!isdevice(brh))
    return NO;

 if (!faultlist){
    gotsize = fcount + 1;
    faultlist = (branch_t*)calloc(gotsize, sizeof(branch_t));
 }else if (fcount + 1 > gotsize){
    gotsize = fcount + 1;
    faultlist=(branch_t*)realloc((void*)faultlist, gotsize*sizeof(branch_t));
 }
 if (!faultlist)
    error(bERROR, e_om, "fault");
 faultlist[fcount] = *brh;
 fcount++;

 brh->val = value;
 brh->x = (generic_t*)NULL;
 brh->subckt = (branch_t*)NULL;

 return YES;
}
/*--------------------------------------------------------------------------*/
void cmd_restore(void)
{
 cmd_unfault();
 cmd_unmark();
}
/*--------------------------------------------------------------------------*/
/* cmd_unfault: (command) remove faults and restore pre-fault values
 * the warnings are notices about known bugs
 * editing a list that has faults can corrupt it.
 */
void cmd_unfault(void)	/* remove all faults, and restore to normal	*/
{			/* can be a command				*/
 branch_t *prev;
 branch_t *next;

 while (fcount-- > 0){
    prev = faultlist[fcount].prev;
    next = faultlist[fcount].next;
    if (prev->next != next->prev)
    	error(bERROR, e_int, "unfault: inconsistent links");
    if (prev != prev->next->prev)
	error(bERROR, e_int, "unfault: missing back link");
    if (next != next->prev->next)
    	error(bERROR, e_int, "unfault: missing fwd link");
    *(prev->next) = faultlist[fcount];
 }
 fcount = 0;
 qfree((void**)&faultlist);
}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
