/*
 * Environment-switching stuff.
 */

#include	<stdio.h>
#ifndef	STRING_H
#define	STRING_H <string.h>
#endif
#include	STRING_H

#include	"etm.h"
#include	"memmgr.h"

#include	"troffcvt.h"


#define	maxEStack	10	/* more than necessary? */


static void	SaveEnvironment (Environ *ep);
static void	RestoreEnvironment (Environ *ep);
static Environ	*FindEnvironment (XChar *name);


/*
 * envList is a list of the currently known environments
 * eStack is a stack of the currently in-use environments
 */

static Environ	*envList = (Environ *) NULL;
static Environ	*eStack[maxEStack];
static int	esTop = -1;


/*
 * Initialize environments with the names "0", "1", and "2" to the
 * current defaults (creating the environments as necessary).  This is 
 * called once before reading any action files to set the environments
 * to the built-in defaults.  It's called again if the default action
 * file is read, to reinitialize the environments to the values in force
 * after the file is read (in case the action file caused a change in
 * the defaults to be used).  This allows the default action file to
 * reset the normal troff defaults (e.g., for legal size paper), and
 * have those values propagate into the three initial environments.
 */

void
InitEnvironments (void)
{
Environ	*ep;
XChar	buf[2];
int	i;

	for (i = 2; i >= 0; i--)
	{
		buf[0] = '0' + i;
		buf[1] = '\0';
		ep = FindEnvironment (buf);
		SaveEnvironment (ep);
	}
	esTop = 0;
	eStack[esTop] = ep;	/* initially in environment named "0" */
}


void
PushEnvironment (XChar *name)
{
Environ	*ep;

	if (esTop + 1 >= maxEStack)
		ETMPanic ("PushEnvironment: environment stack overflow");
	ep = FindEnvironment (name);
	SaveEnvironment (eStack[esTop]);
	eStack[++esTop] = ep;
	RestoreEnvironment (eStack[esTop]);
}


void
PopEnvironment (void)
{
	if (esTop <= 0)
	{
		ETMMsg ("PopEnvironment: environment stack underflow");
		return;
	}
	SaveEnvironment (eStack[esTop]);
	--esTop;
	RestoreEnvironment (eStack[esTop]);
}


/*
 * Save current parameters to a given environment structure
 */

static void
SaveEnvironment (Environ *ep)
{
int	i;

	ep->eSize = curSize;
	ep->ePrevSize = prevSize;
	ep->eSpaceSize = curSpaceSize;
	(void) XStrCpy (ep->eFont, curFont);
	(void) XStrCpy (ep->ePrevFont, prevFont);
	ep->eFillMode = fillMode;
	ep->eAdjust = adjust;
	ep->eAdjMode = adjMode;
	ep->eCenterCount,
	ep->eVSize = curVSize;
	ep->ePrevVSize = prevVSize;
	ep->eLineSpacing = curLineSpacing;
	ep->ePrevLineSpacing = prevLineSpacing;
	ep->eLineLen = curLineLen;
	ep->ePrevLineLen = prevLineLen;
	ep->eIndent = curIndent;
	ep->ePrevIndent = prevIndent;
	ep->eTempIndent = curTempIndent;
	XStrFree (ep->eItMacro);
	if (itMacro == (XChar *) NULL)
		ep->eItMacro = (XChar *) NULL;
	else
		ep->eItMacro = XStrAlloc (itMacro);
	ep->eItCount = itCount;
	ep->eTabCount = curTabCount;
	for (i = 0; i < maxTabStops; i++)
	{
		ep->eTabPos[i] = tabPos[i];
		ep->eTabType[i] = tabType[i];
	}
	ep->eTabChar = tabChar;
	ep->eLeaderChar = leaderChar;
	ep->eUlCount = ulCount;
	ep->eCUlCount = cUlCount;
	ep->eCtrlChar = ctrlChar;
	ep->eNbCtrlChar = nbCtrlChar;
	ep->eHyphenMode = curHyphenMode;
	ep->eHyphenChar = optHyphenChar;
	ep->eTitleLen = curTitleLen;
	ep->ePrevTitleLen = prevTitleLen;
}


/*
 * Restore current parameters from a given environment structure
 */

static void
RestoreEnvironment (Environ *ep)
{
	SetSize (ep->eSize);
	prevSize = ep->ePrevSize;
	SetSpaceSize (ep->eSpaceSize);
	SetFont (ep->eFont);
	(void) XStrCpy (prevFont, ep->ePrevFont);
	SetCFA (ep->eCenterCount, ep->eFillMode, ep->eAdjust, ep->eAdjMode);
	SetSpacing (ep->eVSize);
	prevVSize = ep->ePrevVSize;
	SetLineSpacing (ep->eLineSpacing);
	prevLineSpacing = ep->ePrevLineSpacing;
	SetLineLength (ep->eLineLen);
	prevLineLen = ep->ePrevLineLen;
	SetIndent (ep->eIndent);
	prevIndent = ep->ePrevIndent;
	SetTempIndent (ep->eTempIndent);
	Free ((char *) itMacro);
	if (ep->eItMacro == (XChar *) NULL)
		itMacro = (XChar *) NULL;
	else
		itMacro = XStrAlloc (ep->eItMacro);
	itCount = ep->eItCount;
	SetTabStops (ep->eTabCount, ep->eTabPos, ep->eTabType);
	tabChar = ep->eTabChar;
	leaderChar = ep->eLeaderChar;
	SetUnderline (ep->eUlCount, ep->eCUlCount);
	ctrlChar = ep->eCtrlChar;
	nbCtrlChar = ep->eNbCtrlChar;
	SetHyphenation (ep->eHyphenMode);
	optHyphenChar = ep->eHyphenChar;
	SetTitleLength (ep->eTitleLen);
	prevTitleLen = ep->ePrevTitleLen;
}


/*
 * Find an environment, given its name.  If no environment exists with
 * the given name, create one and initialize it to the current environment
 * settings.
 */

Environ *
FindEnvironment (XChar *name)
{
Environ	*ep;

	for (ep = envList; ep != (Environ *) NULL; ep = ep->eNext)
	{
		if (XStrCmp (name, ep->eName) == 0)	/* found it */
			break;
	}
	if (ep == (Environ *) NULL)			/* didn't find it */
	{
		/*
		 * Didn't find the named environment.  Create it, initialize
		 * the structure members that are pointer-valued and attach
		 * to the list of known environments.  Then initialize to
		 * the current environment values.
		 */
		ep = New (Environ);
		ep->eName = XStrAlloc (name);
		ep->eItMacro = (XChar *) NULL;
		ep->eNext = envList;
		envList = ep;
		SaveEnvironment (ep);
	}
	return (ep);
}


/*
 * Return name of current environment.  Used to implement .ev register,
 * which has a string value.  CANNOT be called before InitEnvironments()
 * has been called.
 */

XChar *
CurrentEnvironmentName (void)
{
	return (eStack[esTop]->eName);
}
