/*
 * Electric(tm) VLSI Design System
 *
 * File: dbcontrol.c
 * Database system controller
 * Written by: Steven M. Rubin, Static Free Software
 *
 * Copyright (c) 2000 Static Free Software.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 *
 * Static Free Software
 * 4119 Alpine Road
 * Portola Valley, California 94028
 * info@staticfreesoft.com
 */

#include "global.h"
#include "database.h"
#include "tech.h"
#include "edialogs.h"

char *el_version = "6.01";		/* the current Electric version */

/* miscellaneous defaults */
#define	DEFTECH     "nmos"        /* default technology */
#define	DEFCONSTR   "layout"      /* default constraint system */

/* prototypes for local routines */
void db_freeallmemory(void);

/*
 * the primary initialization of Electric
 */
void osprimaryosinit(void)
{
	REGISTER INTSML i;
	REGISTER TECHNOLOGY *tech, *lasttech, *realtech;
	REGISTER CONSTRAINT *constr;
	REGISTER CLUSTER *clus;
	REGISTER LIBRARY *lib;
	char libname[10], libfile[10];
	extern TECHNOLOGY el_technologylist[];

	/* initialize the memory allocation system */
	db_initclusters();

	/* internal initialization of the constraint solvers */
	el_curconstraint = &el_constraints[0];
	for(i=0; el_constraints[i].conname != 0; i++)
	{
		constr = &el_constraints[i];
		(void)initinfstr();
		(void)addstringtoinfstr("constraint:");
		(void)addstringtoinfstr(constr->conname);
		constr->cluster = alloccluster(returninfstr());
		if (namesame(constr->conname, DEFCONSTR) == 0) el_curconstraint = constr;
	}

	/* internal initialization of the aids */
	for(el_maxaid=0; el_aids[el_maxaid].aidname != 0; el_maxaid++)
	{
		(void)initinfstr();
		(void)addstringtoinfstr("aid:");
		(void)addstringtoinfstr(el_aids[el_maxaid].aidname);
		el_aids[el_maxaid].cluster = alloccluster(returninfstr());
		el_aids[el_maxaid].aidindex = el_maxaid;
	}

	/* initialize the database */
	db_initdatabase();

	/* internal initialization of the technologies */
	lasttech = NOTECHNOLOGY;
	for(el_maxtech=0; el_technologylist[el_maxtech].techname != 0; el_maxtech++)
	{
		tech = &el_technologylist[el_maxtech];

		/* create the real technology object */
		(void)initinfstr();
		(void)addstringtoinfstr("tech:");
		(void)addstringtoinfstr(tech->techname);
		clus = alloccluster(returninfstr());
		realtech = alloctechnology(clus);
		if (realtech == NOTECHNOLOGY) error("No memory for technologies");

		/* link it in */
		if (lasttech == NOTECHNOLOGY) el_technologies = realtech; else
			lasttech->nexttechnology = realtech;
		lasttech = realtech;

		/* initialize the real technology object */
		(void)allocstring(&realtech->techname, tech->techname, clus);
		realtech->techindex = el_maxtech;
		realtech->deflambda = tech->deflambda;
		realtech->parse = tech->parse;
		realtech->cluster = clus;
		(void)allocstring(&realtech->techdescript, tech->techdescript, clus);
		realtech->layercount = tech->layercount;
		realtech->layers = tech->layers;
		realtech->arcprotocount = tech->arcprotocount;
		realtech->arcprotos = tech->arcprotos;
		realtech->nodeprotocount = tech->nodeprotocount;
		realtech->nodeprotos = tech->nodeprotos;
		realtech->variables = tech->variables;
		realtech->init = tech->init;
		realtech->term = tech->term;
		realtech->setmode = tech->setmode;
		realtech->request = tech->request;
		realtech->paramnode = tech->paramnode;
		realtech->nodepolys = tech->nodepolys;
		realtech->nodeEpolys = tech->nodeEpolys;
		realtech->shapenodepoly = tech->shapenodepoly;
		realtech->shapeEnodepoly = tech->shapeEnodepoly;
		realtech->nodesizeoffset = tech->nodesizeoffset;
		realtech->shapeportpoly = tech->shapeportpoly;
		realtech->arcpolys = tech->arcpolys;
		realtech->shapearcpoly = tech->shapearcpoly;
		realtech->arcwidthoffset = tech->arcwidthoffset;
		realtech->userbits = tech->userbits;
	}

	/* select a current technology */
	el_curtech = el_technologies;
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		if (namesame(tech->techname, DEFTECH) == 0) el_curtech = tech;

	/* setup a change batch for initialization work */
	/* db_preparenewbatch(&el_aids[0]); */

	/* pass 1 initialization of the constraint solvers */
	for(i=0; el_constraints[i].conname != 0; i++)
		(*el_constraints[i].init)(&el_constraints[i]);

	/* pass 1 initialization of technologies */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
	{
		if (tech->init != 0)
		{
			if ((*tech->init)(tech, 0) != 0)
				error("User pass 1 error initializing %s technology", tech->techname);
		}
		if (tech_doinitprocess(tech) != 0)
			error("Pass 1 error initializing %s technology", tech->techname);
	}

	/* pass 1 initialization of the aids */
	for(i=0; i<el_maxaid; i++)
		(*el_aids[i].init)(0, 0, &el_aids[i]);

	/* create the first library */
	el_curlib = NOLIBRARY;
	strcpy(libname, "noname");
	strcpy(libfile, "noname");
	lib = newlibrary(libname, libfile);
	selectlibrary(lib);

	/* establish the library location */
	setupenvironment();
}

/*
 * the secondary initialization of Electric
 */
void ossecondaryinit(INTBIG argc, char *argv[])
{
	REGISTER INTSML i;
	REGISTER TECHNOLOGY *tech;

	/* pass 2 initialization of the constraint solvers */
	for(i=0; el_constraints[i].conname != 0; i++)
		(*el_constraints[i].init)(NOCONSTRAINT);

	/* pass 2 initialization of technologies */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
	{
		if (tech->init != 0)
		{
			if ((*tech->init)(tech, 1) != 0)
				error("User pass 2 error initializing %s technology", tech->techname);
		}
		if (tech_doaddportsandvars(tech))
			error("Pass 2 error initializing %s technology", tech->techname);
	}

	/* pass 2 initialization of the aids */
	for(i=0; i<el_maxaid; i++) (*el_aids[i].init)(&argc, argv, NOAID);

	/* pass 3 initialization of technologies */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
	{
		if (tech->init != 0)
		{
			if ((*tech->init)(tech, 2) != 0)
				error("User pass 3 error initializing %s technology", tech->techname);
		}
	}

	/* pass 3 initialization of the aids */
	for(i=0; i<el_maxaid; i++) (*el_aids[i].init)(&argc, argv, 0);

	/* register and initialize technology-variable caching functions */
	db_inittechcache();

	/* delete initialization change batches so it is ready for real changes */
	noundoallowed();
}

/* the body of the main loop of Electric */
void tooltimeslice(void)
{
	REGISTER INTSML s;
	REGISTER AIDENTRY *aid;

	for(s=0; s<el_maxaid; s++)
	{
		aid = &el_aids[s];
		if ((aid->aidstate & AIDON) == 0) continue;
		if (aid->slice == 0) continue;

		/* let the aid have a whack */
		db_setcurrenttool(aid);
		(*aid->slice)();

		/* announce end of broadcast of changes */
		db_endbatch();
	}
}

INTSML tellaid(AIDENTRY *aid, INTSML count, char *par[])
{
	if (aid->setmode == 0) return(0);
	return((*aid->setmode)(count, par));
}

INTBIG askaid(AIDENTRY *aid, char *command, ...)
{
	va_list ap;
	INTBIG result;

	if (aid->request == 0) return(0);
	var_start(ap, command);
	result = (*aid->request)(command, ap);
	va_end(ap);
	return(result);
}

/*
 * routine to turn aid "aid" back on.  It will also inform the aid of each
 * facet that changed while the aid was off (via "examinenodeproto").  If
 * "nocatchup" is nonzero, this informing will not occur.
 */
void aidturnon(AIDENTRY *aid, INTSML nocatchup)
{
	REGISTER INTBIG bit;
	REGISTER NODEPROTO *np;
	REGISTER LIBRARY *lib;

	/* turn on the aid */
	(void)setval((INTBIG)aid, VAID, "aidstate", aid->aidstate|AIDON, VINTEGER);

	/* if the aid is not incremental, don't inform it of changes while off */
	if ((aid->aidstate & AIDINCREMENTAL) == 0) return;

	/* if catch-up action is supressed, quit now */
	if (nocatchup != 0) return;

	/* look through all facets and update those that need it */
	bit = 1 << aid->aidindex;
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		if (np->adirty & bit)
		{
			if (aid->examinenodeproto != 0)
				(*aid->examinenodeproto)(np);
		}
		np->adirty &= ~bit;
	}
}

/*
 * routine to turn aid "aid" off.  If "permanently" is nonzero, this change
 * will not be undoable
 */
void aidturnoff(AIDENTRY *aid, INTSML permanently)
{
	/* turn off the aid */
	if (permanently != 0) aid->aidstate &= ~AIDON; else
		(void)setval((INTBIG)aid, VAID, "aidstate", aid->aidstate & ~AIDON, VINTEGER);
}

/*
 * shutdown the design aid
 */
void bringdown(void)
{
	REGISTER INTSML i;
	REGISTER TECHNOLOGY *tech;
	REGISTER AIDENTRY *aid;
	REGISTER CONSTRAINT *con;

	/* go backwards through aids to shut down in proper order */
	for(i = el_maxaid-1; i >= 0; i--)
	{
		aid = &el_aids[i];
		if (aid->done != 0) (*aid->done)();
		if (aid->numvar != 0) db_freevars(&aid->firstvar, &aid->numvar);
	}

	/* terminate technologies */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		if (tech->term != 0) (*tech->term)();

	/* terminate any language interpreters */
	db_termlanguage();

	/* shut down the constraint solvers */
	for(i=0; el_constraints[i].conname != 0; i++)
	{
		con = &el_constraints[i];
		(*con->term)();
		if (con->numvar != 0) db_freevars(&con->firstvar, &con->numvar);
	}

	/* free up all memory */
	db_freeallmemory();

	/* check that there were no leaks */
	db_checkallmemoryfree();

	exitprogram();
}

void db_freeallmemory(void)
{
#ifdef DEBUGMEMORY
	REGISTER POLYGON *poly;
	REGISTER VIEW *v;
	REGISTER TECHNOLOGY *tech, *nexttech;
	REGISTER LIBRARY *lib;

	noundoallowed();

	/* free all statically allocated polygons */
	while (db_staticpolygons != NOPOLYGON)
	{
		poly = db_staticpolygons;
		db_staticpolygons = db_staticpolygons->nextpolygon;
		freepolygon(poly);
	}

	/* free all views */
	while (el_views != NOVIEW)
	{
		v = el_views;
		el_views = el_views->nextview;
		efree((char *)v->viewname);
		efree((char *)v->sviewname);
		freeview(v);
	}

	/* free all technologies */
	for (tech = el_technologies; tech != NOTECHNOLOGY; tech = nexttech)
	{
		nexttech = tech->nexttechnology;
		freetechnology(tech);
	}

	/* free all libraries */
	while (el_curlib != NOLIBRARY)
	{
		lib = el_curlib;
		el_curlib = el_curlib->nextlibrary;
		eraselibrary(lib);
		efree(lib->libfile);
		efree(lib->libname);
		freelibrary(lib);
	}

	/* free all database memory */
	db_freetechnologymemory();
	db_freechangememory();
	db_freetextmemory();
	db_freevariablememory();
	db_freenoprotomemory();
	db_freemathmemory();
	db_freemergememory();
	db_freegeomemory();
#  if LANGTCL
	db_freetclmemory();
#  endif
#endif
}

INTSML stopping(char *who)
{
	checkforinterrupt();
	if (el_pleasestop != 1) return(el_pleasestop);
	el_pleasestop = 0;
	flushscreen();
	ttyputmsg("%s stopped", who);
	el_pleasestop = 2;
	return(el_pleasestop);
}
