/*
	trf-stack.c - internal and written stack operations.
*/

# include	<stdio.h>
# include	<sys/types.h>
# include	"rtf.h"
# include	"rtf2troff.h"


static int	iTopState = 0;		/* current internal state */
static State	iState[maxIStack] =	/* internal state stack */
{
/*
	state 0 = initial state.
*/
		rtfNoDestination,	/* destination */
	{
		0,			/* landscape (0 = no) */
		12240.0/rtfTpi,		/* paper width = 8.5i*/
		15840.0/rtfTpi,		/* paper height = 11i */
		1800.0/rtfTpi,		/* left margin = 1.25i */
		1800.0/rtfTpi,		/* right margin = 1.25i */
		1440.0/rtfTpi,		/* top margin = 1i */
		1440.0/rtfTpi,		/* bottom margin = 1i */
		720.0/rtfTpi		/* tab width = .5i */
	},
	{
		rtfPageBreak,		/* section break type */
		1,			/* starting page number */
		0,			/* continuous page numbering */
		1080.0/rtfTpi,		/* header position */
		1080.0/rtfTpi,		/* footer position */
		0			/* title page not special */
	},
	{
		0,			/* first indent */
		0,			/* left indent */
		0,			/* right indent */
		0,			/* space before */
		0,			/* space after */
		.2,			/* space between: 12p * 1.2 = 14.4p */
		0,			/* no tabs set yet */
		0,			/* number of tabs */
		{ 0 },			/* tab positions */
		{ 0 },			/* tab types */
		rtfLeaderMotion,	/* tab character */
		rtfQuadLeft,		/* justification */
		rtfNoBorderType,	/* no border */
		0			/* draw border nowhere */
	},
	{
		12,			/* font size */
		0,			/* char style (plain, DON'T CHANGE) */
		0,			/* superscript */
		0			/* subscript */
	}
};


static int	wTopState = 0;		/* current written state */
static State	wState[maxWStack];	/* written state stack */


/*
	Set up pointers into internal state 0, and equate initial written
	state to internal state (although written state isn't actually
	written until FlushInitialState()).

	Initialize the tab type array to left tabs, so that any tab
	positions specified without a type will default to left-justified.
*/

void InitState ()
{
	is = &iState[0];
	/* initialize state 0 */
	ids = &is->docState;
	iss = &is->sectState;
	ips = &is->parState;
	ics = &is->charState;
	ips->tabFlag = 0;
	InitTabSet ();
	/* sync written state to internal state */
	bcopy ((char *) &iState[0], (char *) &wState[0], (int) sizeof (State));
	ws = &wState[0];
	wds = &ws->docState;
	wss = &ws->sectState;
	wps = &ws->parState;
	wcs = &ws->charState;
}


void CheckFinalState ()
{
	if (iTopState != 0)
		fprintf (stderr, "Warning: unbalanced brace level\n");
	if (wTopState != 0)
		fprintf (stderr, "Warning: unrestored environment\n");
	if (indirectionLevel > 0)
		fprintf (stderr, "Warning: unrestored indirection\n");
}


/*
	Push or pop internal state.

	On push, initial value of new state is same as current state, so
	no state *change* is involved.  Indicate that no destination or
	tab stops have been specified.

	On pop, revert to previous state.  It's just laziness to set the
	state change variables on a state pop, since some or all of them
	may well not have changed at all... but it's safest and easiest
	to do so.
*/


void PushIState ()
{
	if (iTopState >= maxIStack - 1)
	{
		fprintf (stderr, "Internal state stack limit exceeded");
		fprintf (stderr, " maximum level (%d)\n", maxIStack);
		exit (1);
	}
	bcopy ((char *) &iState[iTopState],
		(char *) &iState[iTopState + 1], (int) sizeof (State));
	is = &iState[++iTopState];
	is->destination = rtfNoDestination;
	is->parState.tabFlag = 0;	/* no tabs set in state yet */
	ids = &is->docState;
	iss = &is->sectState;
	ips = &is->parState;
	ics = &is->charState;
}


void PopIState ()
{
	if (iTopState < 1)
	{
		fprintf (stderr, "Pop error: no internal state to pop");
		fprintf (stderr, " maximum level (%d)\n", maxIStack);
		exit (1);
	}
	is = &iState[--iTopState];
	ids = &is->docState;
	iss = &is->sectState;
	ips = &is->parState;
	ics = &is->charState;
	++docStateChanged;
	++sectStateChanged;
	++parStateChanged;
	++charStateChanged;
}


void PushWState ()
{
	if (wTopState >= maxWStack - 1)
	{
		fprintf (stderr, "Written state stack limit exceeded");
		fprintf (stderr, " maximum level (%d)\n", maxWStack);
		exit (1);
	}
	bcopy ((char *) &wState[wTopState],
		(char *) &wState[wTopState + 1], (int) sizeof (State));
	ws = &wState[++wTopState];
	wds = &ws->docState;
	wss = &ws->sectState;
	wps = &ws->parState;
	wcs = &ws->charState;
}


void PopWState ()
{
	if (wTopState < 1)
	{
		fprintf (stderr, "Pop error: no written state to pop");
		fprintf (stderr, " maximum level (%d)\n", maxWStack);
		exit (1);
	}
	ws = &wState[--wTopState];
	wds = &ws->docState;
	wss = &ws->sectState;
	wps = &ws->parState;
	wcs = &ws->charState;
	++docStateChanged;
	++sectStateChanged;
	++parStateChanged;
	++charStateChanged;
}


/*
	Environment switching routines.  When commands are written
	to switch environments, take snapshot of current written state.
	When environment switches back, restore to snapshot state to
	reflect the troff state switch.

	Environment switches are saved only when diversions are collected,
	not when they are written out.
*/


void BeginDiversion (name)
char	*name;
{
	Flush ();
	fprintf (f, ".rm %s\n.di %s\n", name, name);
	++indirectionLevel;
	fprintf (f, ".ev 1\n");
	PushWState ();
}


void EndDiversion ()
{
	Flush ();
	fprintf (f, ".br\n");
	fprintf (f, ".ev\n");
	PopWState ();
	fprintf (f, ".di\n");
	--indirectionLevel;
}


/*
	Restore section, paragraph or character defaults, using
	values stored in state 0.

	Paragraph defaults are restored by using the state 0 values,
	they applying the "Normal" style (style 0).  The tab flag is reset
	before expanding the style so any inherited tabs will be overridden
	by tabs in the style, and reset after expansion so any tabs in the
	paragraph itself will override inherited or style tabs.
*/


void RestoreSectDefaults ()
{
	bcopy ((char *) &iState[0].sectState, (char *) iss,
						(int) sizeof (SectState));
}


void RestoreParDefaults ()
{
	bcopy ((char *) &iState[0].parState, (char *) ips,
					(int) sizeof (ParState));
	ips->tabFlag = 0;
	RTFExpandStyle (0);
	ips->tabFlag = 0;
}


void RestoreCharDefaults ()
{
	bcopy ((char *) &iState[0].charState, (char *) ics,
						(int) sizeof (CharState));
}