#include "lsidefs.h"
#include "pilsobj.h"
#include "plsline.h"
#include "lstext.h"
#include "lscbk.h"
#include "lsc.h"
#include "lscontxt.h"
#include "limqmem.h"
#include "qheap.h"
#include "lsline.h"
#include "lsdnode.h"
#include "iobj.h"
#include "chnutils.h"
#include "autonum.h"

#include "lsmem.h"						/* memset() */



static LSERR CannotCreateLsContext(PLSC, LSERR);
static LSERR  InitObject(PLSC plsc, DWORD iobj, const LSIMETHODS* plsim);
static LSERR RemoveContextObjects(PLSC plsc);

#ifdef DEBUG
#ifdef LSTEST_ASSERTSTOP

/* We use it to run debug LS with ship build of WORD */

int nZero = 0;

void AssertFailedStop (char* pzstrMsg, char* pzstrFile, int nLine)
{
	Unreferenced (pzstrMsg);
	Unreferenced (pzstrFile);
	Unreferenced (nLine);

	nZero = nZero / nZero;

	return;
}

#endif
#endif


/* L S  C R E A T E  C O N T E X T */
/*----------------------------------------------------------------------------
    %%Function: LsCreateContext
    %%Contact: igorzv

Parameters:
	plsci 		-   (IN) structure which contains clients setings
	pplsc		-   (OUT) pointer to created contexts (opaque to clients)

    Creates a Line Services context.
	Typically called once, at the beginning of time.
----------------------------------------------------------------------------*/
LSERR WINAPI LsCreateContext(const LSCONTEXTINFO* plsci, PLSC* pplsc)
{
	static LSIMETHODS const lsimText = 
	{
		CreateILSObjText,
		DestroyILSObjText,
		SetDocText,
		CreateLNObjText,
		DestroyLNObjText,
		FmtText,
		NULL,
		NULL,         
		NULL,
		TruncateText,
		FindPrevBreakText,
		FindNextBreakText,
		ForceBreakText,
		SetBreakText,
		NULL,
		NULL,
		NULL,
		CalcPresentationText,
		QueryPointPcpText,
		QueryCpPpointText,
		EnumObjText,
		DisplayText,
		DestroyDObjText,
	};

	static LSIMETHODS const lsimAutonum = 
	{
		AutonumCreateILSObj,
		AutonumDestroyILSObj,
		AutonumSetDoc,
		AutonumCreateLNObj,
		AutonumDestroyLNObj,
		AutonumFmt,
		NULL,
		NULL,         
		NULL,
		AutonumTruncateChunk,
		AutonumFindPrevBreakChunk,
		AutonumFindNextBreakChunk,
		AutonumForceBreakChunk,
		AutonumSetBreak,
		AutonumGetSpecialEffectsInside,
		NULL,
		NULL,
		AutonumCalcPresentation,
		AutonumQueryPointPcp,
		AutonumQueryCpPpoint,
		AutonumEnumerate,
		AutonumDisplay,
		AutonumDestroyDobj,
	};

	DWORD const iobjText = plsci->cInstalledHandlers;
	DWORD const iobjAutonum = plsci->cInstalledHandlers + 1; 
	DWORD const iobjMac = iobjText + 2;
	POLS const pols = plsci->pols;
	const LSIMETHODS* const plsim = plsci->pInstalledHandlers;

	DWORD iobj;
	PLSC plsc;
	LSERR lserr;
	

#ifdef DEBUG
#ifdef LSTEST_ASSERTSTOP

	/* We use this option when run debug LS with ship WORD */

	pfnAssertFailed = AssertFailedStop;

#else

    pfnAssertFailed = plsci->lscbk.pfnAssertFailed;

#endif
#endif

	if (pplsc == NULL)
		return lserrNullOutputParameter;

	*pplsc = NULL;

	/* Allocate memory for the context and clean it
	 */
	plsc = plsci->lscbk.pfnNewPtr(pols, cbRep(struct lscontext, lsiobjcontext.rgobj, iobjMac));
	if (plsc == NULL)
		return lserrOutOfMemory;
	memset(plsc, 0, cbRep(struct lscontext, lsiobjcontext.rgobj, iobjMac)); 

	/* Initialize the fixed-size part of the context
	 */
	plsc->tag = tagLSC;
	plsc->pols = pols;
	plsc->lscbk = plsci->lscbk;
	plsc->fDontReleaseRuns = plsci->fDontReleaseRuns;
	

	plsc->cLinesActive = 0;
	plsc->plslineCur = NULL;

	plsc->lsstate = LsStateCreatingContext;

	plsc->pqhLines = CreateQuickHeap(plsc, limLines,
									 cbRep(struct lsline, rgplnobj, iobjMac), fFalse);
	plsc->pqhAllDNodesRecycled = CreateQuickHeap(plsc, limAllDNodes,
										 sizeof (struct lsdnode), fTrue);
	if (plsc->pqhLines == NULL || plsc->pqhAllDNodesRecycled == NULL )
		{
		return CannotCreateLsContext(plsc, lserrOutOfMemory);
		}


	/* create arrays for chunks  */
	lserr = AllocChunkArrays(&plsc->lschunkcontextStorage, &plsc->lscbk, plsc->pols,
		&plsc->lsiobjcontext);
	if (lserr != lserrNone)
		return CannotCreateLsContext(plsc, lserr);


	/* create array for tabs  */
	plsc->lstabscontext.pcaltbd = plsci->lscbk.pfnNewPtr(pols, 
											sizeof(LSCALTBD)*limCaltbd);

	plsc->lstabscontext.ccaltbdMax = limCaltbd;

	if (plsc->lstabscontext.pcaltbd == NULL )
		{
		return CannotCreateLsContext(plsc, lserrOutOfMemory);
		}

	/*  set links in lstabscontext */
	plsc->lstabscontext.plscbk = &plsc->lscbk;
	plsc->lstabscontext.pols = plsc->pols;
	plsc->lstabscontext.plsdocinf = &plsc->lsdocinf;


	/* ****************************************************************** */
	/* Initialize the "static" array part of the context
	 * "Text" is the last element of the array
	 */
	plsc->lsiobjcontext.iobjMac = iobjMac;
	for (iobj = 0;  iobj < iobjText;  iobj++)
		{
		lserr = InitObject(plsc, iobj, &plsim[iobj]);
		if (lserr != lserrNone)
			return CannotCreateLsContext(plsc, lserr);
		}

	lserr = InitObject(plsc, iobjText, &lsimText);
	if (lserr != lserrNone)
		return CannotCreateLsContext(plsc, lserr);

	/* Set text Config				*/
	lserr = SetTextConfig(PilsobjFromLsc(&plsc->lsiobjcontext, iobjText), &(plsci->lstxtcfg));
	if (lserr != lserrNone)
		return CannotCreateLsContext(plsc, lserr);

	lserr = InitObject(plsc, iobjAutonum, &lsimAutonum);
	if (lserr != lserrNone)
		return CannotCreateLsContext(plsc, lserr);

	/* Set text Config				*/
	lserr = SetAutonumConfig(PilsobjFromLsc(&plsc->lsiobjcontext, iobjAutonum), 
					&(plsci->lstxtcfg));
	if (lserr != lserrNone)
		return CannotCreateLsContext(plsc, lserr);


	plsc->lsstate = LsStateNotReady;  /* nobody can use context before LsSetDoc  */


	/* we set other variavles by memset, bellow we check that we get what we want  */
	Assert(plsc->cLinesActive == 0);
	Assert(plsc->plslineCur == NULL);
	Assert(plsc->fIgnoreSplatBreak == 0);
	Assert(plsc->fLimSplat == fFalse);
	Assert(plsc->fHyphenated == fFalse);
	Assert(plsc->fAdvanceBack == fFalse);
	Assert(plsc->grpfManager == 0);
	Assert(plsc->urRightMarginBreak == 0);
	Assert(plsc->lMarginIncreaseCoefficient == 0);


	Assert(plsc->lsdocinf.fDisplay == fFalse);
	Assert(plsc->lsdocinf.fPresEqualRef == fFalse);
	Assert(plsc->lsdocinf.lsdevres.dxpInch == 0);
	Assert(plsc->lsdocinf.lsdevres.dxrInch == 0);
	Assert(plsc->lsdocinf.lsdevres.dypInch == 0);
	Assert(plsc->lsdocinf.lsdevres.dyrInch == 0);

	Assert(plsc->lstabscontext.fTabsInitialized == fFalse);
	Assert(plsc->lstabscontext.durIncrementalTab == 0);
	Assert(plsc->lstabscontext.urBeforePendingTab == 0);
	Assert(plsc->lstabscontext.plsdnPendingTab == NULL);
	Assert(plsc->lstabscontext.icaltbdMac == 0);
	Assert(plsc->lstabscontext.urColumnMax == 0);
	Assert(plsc->lstabscontext.fResolveTabsAsWord97 == fFalse);

	Assert(plsc->lsadjustcontext.fLineCompressed == fFalse);
	Assert(plsc->lsadjustcontext.fLineContainsAutoNumber == fFalse);
	Assert(plsc->lsadjustcontext.fUnderlineTrailSpacesRM == fFalse);
	Assert(plsc->lsadjustcontext.fForgetLastTabAlignment == fFalse);
	Assert(plsc->lsadjustcontext.fNominalToIdealEncounted == fFalse);
	Assert(plsc->lsadjustcontext.fForeignObjectEncounted == fFalse);
	Assert(plsc->lsadjustcontext.fTabEncounted == fFalse);
	Assert(plsc->lsadjustcontext.fNonLeftTabEncounted == fFalse);
	Assert(plsc->lsadjustcontext.fSubmittedSublineEncounted == fFalse);
	Assert(plsc->lsadjustcontext.fAutodecimalTabPresent == fFalse);
	Assert(plsc->lsadjustcontext.lskj == lskjNone);
	Assert(plsc->lsadjustcontext.lskalign == lskalLeft);
	Assert(plsc->lsadjustcontext.lsbrj == lsbrjBreakJustify);
	Assert(plsc->lsadjustcontext.urLeftIndent == 0);
	Assert(plsc->lsadjustcontext.urStartAutonumberingText == 0);
	Assert(plsc->lsadjustcontext.urStartMainText == 0);
	Assert(plsc->lsadjustcontext.urRightMarginJustify == 0);

	Assert(plsc->lschunkcontextStorage.FChunkValid == fFalse);
	Assert(plsc->lschunkcontextStorage.FLocationValid == fFalse);
	Assert(plsc->lschunkcontextStorage.FGroupChunk == fFalse);
	Assert(plsc->lschunkcontextStorage.FBorderInside == fFalse);
	Assert(plsc->lschunkcontextStorage.grpfTnti == 0);
	Assert(plsc->lschunkcontextStorage.fNTIAppliedToLastChunk == fFalse);
	Assert(plsc->lschunkcontextStorage.locchnkCurrent.clschnk == 0);
	Assert(plsc->lschunkcontextStorage.locchnkCurrent.lsfgi.fFirstOnLine == fFalse);
	Assert(plsc->lschunkcontextStorage.locchnkCurrent.lsfgi.cpFirst == fFalse);
	Assert(plsc->lschunkcontextStorage.locchnkCurrent.lsfgi.urPen == 0);
	Assert(plsc->lschunkcontextStorage.locchnkCurrent.lsfgi.vrPen == 0);
	Assert(plsc->lschunkcontextStorage.locchnkCurrent.lsfgi.urColumnMax == 0);
	Assert(plsc->lschunkcontextStorage.locchnkCurrent.lsfgi.lstflow == 0);


	Assert(plsc->lslistcontext.plsdnToFinish == NULL);
	Assert(plsc->lslistcontext.plssublCurrent == NULL);
	Assert(plsc->lslistcontext.nDepthFormatLineCurrent == 0);

	/* Everything worked, so set the output parameter and return success
	 */
	*pplsc = plsc;
	return lserrNone;
}

/* C A N N O T  C R E A T E  L S  C O N T E X T */
/*----------------------------------------------------------------------------
    %%Function: CannotCreateLsContext
    %%Contact: igorzv

Parameters:
	plsc		-	partually created context
	lseReturn 	-	error code

    Utility function called when an error occurs when an LSC is
	partially created.
----------------------------------------------------------------------------*/
static LSERR CannotCreateLsContext(PLSC plsc, LSERR lseReturn)
{
	plsc->lsstate = LsStateFree;   /* otherwise destroy will not work */
	(void) LsDestroyContext(plsc);
	return lseReturn;
}




/* L S  D E S T R O Y  C O N T E X T */
/*----------------------------------------------------------------------------
    %%Function: LsDestroyContext
    %%Contact: igorzv

Parameters:
	plsc		-	(IN) ptr to line services context 

    Frees all resources associated with a Line Services context,
	which was created by CreateLsContext.
----------------------------------------------------------------------------*/

LSERR WINAPI LsDestroyContext(PLSC plsc) 
{
	LSERR lserr = lserrNone;

	if (plsc != NULL)
		{
		if (!FIsLSC(plsc))
			return lserrInvalidContext;

		if (plsc->cLinesActive != 0 || FIsLSCBusy(plsc))
			return lserrContextInUse;

		plsc->lsstate = LsStateDestroyingContext;

		DestroyQuickHeap(plsc->pqhLines);
		Assert(plsc->pqhAllDNodesRecycled != NULL);
		DestroyQuickHeap(plsc->pqhAllDNodesRecycled);

		DisposeChunkArrays(&plsc->lschunkcontextStorage);
		
		plsc->lscbk.pfnDisposePtr(plsc->pols, plsc->lstabscontext.pcaltbd);


		lserr = RemoveContextObjects(plsc);


		plsc->tag = tagInvalid;
		plsc->lscbk.pfnDisposePtr(plsc->pols, plsc);
		}

	return lserr;
}

 static LSERR  InitObject(PLSC plsc, DWORD iobj, const LSIMETHODS* plsim)
{
	struct OBJ *pobj;
	LSERR lserr;
	
	Assert(FIsLSC(plsc));
	Assert(plsc->lsstate == LsStateCreatingContext);
	Assert(iobj < plsc->lsiobjcontext.iobjMac);

	pobj = &(plsc->lsiobjcontext.rgobj[iobj]);
	pobj->lsim = *plsim;
	Assert(pobj->pilsobj == NULL); 

	lserr = pobj->lsim.pfnCreateILSObj(plsc->pols, plsc, &(plsc->lscbk), iobj, &(pobj->pilsobj));
	if (lserr != lserrNone)
		{
		if (pobj->pilsobj != NULL)
			{
			pobj->lsim.pfnDestroyILSObj(pobj->pilsobj);
			pobj->pilsobj = NULL;
			}
		return lserr;
		}

	return lserrNone;   
	
}
/* R E M O V E  C O N T E X T  O B J E C T S */
/*----------------------------------------------------------------------------
    %%Function: RemoveContextObjects
    %%Contact: igorzv
Parameter:
	plsc		-	(IN) ptr to line services context 

    Removes a set of installed objects from an LSC.
	Destroy all ilsobj 
----------------------------------------------------------------------------*/
LSERR RemoveContextObjects(PLSC plsc)
{
	DWORD iobjMac;
	LSERR lserr, lserrFinal = lserrNone;
	DWORD iobj;
	PILSOBJ pilsobj;

	Assert(FIsLSC(plsc));
	Assert(plsc->lsstate == LsStateDestroyingContext);

	iobjMac = plsc->lsiobjcontext.iobjMac;
	
	for (iobj = 0;  iobj < iobjMac;  iobj++)
		{
		pilsobj = plsc->lsiobjcontext.rgobj[iobj].pilsobj;
		if (pilsobj != NULL)
			{
			lserr = plsc->lsiobjcontext.rgobj[iobj].lsim.pfnDestroyILSObj(pilsobj);
			plsc->lsiobjcontext.rgobj[iobj].pilsobj = NULL;
			if (lserr != lserrNone)
				lserrFinal = lserr;
			}
		}

	return lserrFinal;	
}


#ifdef DEBUG
/* F  I S  L S C O N T E X T   V A L I D*/
/*----------------------------------------------------------------------------
    %%Function: FIsLsContextValid
    %%Contact: igorzv

Parameters:
	plsc		-	(IN) ptr to line services context 

this function verify that nobody spoiled context, all reasonable integrity checks 
should be here 																		
----------------------------------------------------------------------------*/


BOOL FIsLsContextValid(PLSC plsc)
{
	DWORD iobjText = IobjTextFromLsc(&plsc->lsiobjcontext);
	Assert(plsc->lsiobjcontext.rgobj[iobjText].lsim.pfnCreateILSObj ==CreateILSObjText);
	Assert(plsc->lsiobjcontext.rgobj[iobjText].lsim.pfnDestroyILSObj == DestroyILSObjText);
	Assert(plsc->lsiobjcontext.rgobj[iobjText].lsim.pfnSetDoc == SetDocText);
	Assert(plsc->lsiobjcontext.rgobj[iobjText].lsim.pfnCreateLNObj == CreateLNObjText);
	Assert(plsc->lsiobjcontext.rgobj[iobjText].lsim.pfnDestroyLNObj == DestroyLNObjText);
	Assert(plsc->lsiobjcontext.rgobj[iobjText].lsim.pfnTruncateChunk == TruncateText);
	Assert(plsc->lsiobjcontext.rgobj[iobjText].lsim.pfnFindPrevBreakChunk == FindPrevBreakText);
	Assert(plsc->lsiobjcontext.rgobj[iobjText].lsim.pfnFindNextBreakChunk == FindNextBreakText);
	Assert(plsc->lsiobjcontext.rgobj[iobjText].lsim.pfnForceBreakChunk == ForceBreakText);
	Assert(plsc->lsiobjcontext.rgobj[iobjText].lsim.pfnDisplay == DisplayText);
	Assert(plsc->lsiobjcontext.rgobj[iobjText].lsim.pfnQueryPointPcp == QueryPointPcpText);
	Assert(plsc->lsiobjcontext.rgobj[iobjText].lsim.pfnQueryCpPpoint == QueryCpPpointText);
	Assert(plsc->lsiobjcontext.rgobj[iobjText].lsim.pfnDestroyDObj == DestroyDObjText);
	Assert(plsc->lschunkcontextStorage.pcont != NULL);
	Assert(plsc->lschunkcontextStorage.pplsdnChunk != NULL);
	Assert(plsc->lschunkcontextStorage.locchnkCurrent.plschnk != NULL);
	Assert(plsc->lschunkcontextStorage.pplsdnNonText != NULL);
	Assert(plsc->lschunkcontextStorage.pfNonTextExpandAfter != NULL);
	Assert(plsc->lschunkcontextStorage.pdurOpenBorderBefore != NULL);
	Assert(plsc->lschunkcontextStorage.pdurCloseBorderAfter != NULL);

	return fTrue; /* if we here than everything OK  */
}
#endif