/*
 *	@doc INTERNAL
 *
 *	@module	OLSOLE.CPP -- OlsOle LineServices object class
 *	
 *	Author:
 *		Murray Sargent (with lots of help from RickSa's ols code)
 *
 *	Copyright (c) 1997-2000 Microsoft Corporation. All rights reserved.
 */

#include "_common.h"

#ifndef NOLINESERVICES

#include "_font.h"
#include "_edit.h"
#include "_disp.h"
#include "_ols.h"
#include "_render.h"
extern "C" {
#include "objdim.h"
#include "pobjdim.h"
#include "plsdnode.h"
#include "dispi.h"
#include "pdispi.h"
#include "fmti.h"
#include "lsdnset.h"
#include "lsdnfin.h"
#include "brko.h"
#include "pbrko.h"
#include "locchnk.h"
#include "lsqout.h"
#include "lsqin.h"
#include "lsimeth.h"
}

extern BOOL g_OLSBusy;

/*
 *	OlsOleCreateILSObj(pols, plsc, pclscbk, dword, ppilsobj)
 *
 *	@func
 *		Create LS Ole object handler. We don't have any need for
 *		this, so just set it to 0.
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsOleCreateILSObj(
	POLS	 pols,		//[IN]: COls * 
	PLSC	 plsc,  	//[IN]: LineServices context
	PCLSCBK,
	DWORD,
	PILSOBJ *ppilsobj)	//[OUT]: ptr to ilsobj
{
	*ppilsobj = 0;
	return lserrNone;
}

/*
 *	OlsOleDestroyILSObj(pilsobj)
 *
 *	@func
 *		Destroy LS Ole handler object. Nothing to do, since we don't
 *		use the ILSObj.
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsOleDestroyILSObj(
	PILSOBJ pilsobj)
{
	return lserrNone;
}

/*
 *	OlsOleSetDoc(pilsobj, pclsdocinf)
 *
 *	@func
 *		Set doc info. Nothing to do for Ole objects
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsOleSetDoc(
	PILSOBJ, 
	PCLSDOCINF)
{
	// Ole objects don't care about this
	return lserrNone;
}

/*
 *	OlsOleCreateLNObj(pilsobj, pplnobj)
 *
 *	@func
 *		Create the line object. Nothing needed in addition to the ped,
 *		so just return the ped as the LN object.
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsOleCreateLNObj(
	PCILSOBJ pilsobj, 
	PLNOBJ * pplnobj)
{
	*pplnobj = (PLNOBJ)g_pols->_pme->GetPed();			// Just the ped
	return lserrNone;
}

/*
 *	OlsOleDestroyLNObj(plnobj)
 *
 *	@func
 *		Destroy LN object. Nothing to do, since ped is destroyed
 *		elsewhere
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsOleDestroyLNObj(
	PLNOBJ plnobj)
{
	return lserrNone;
}

/*
 *	OlsOleFmt(plnobj, pcfmtin, pfmres)
 *
 *	@func
 *		Compute dimensions of a particular Ole object
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsOleFmt(
	PLNOBJ	plnobj, 
	PCFMTIN pcfmtin, 
	FMTRES *pfmres)
{
	const LONG		cp = pcfmtin->lsfrun.plsrun->_cp; //Cannot trust LS cps
	LONG			dup = 0;
	LSERR			lserr;
	OBJDIM			objdim;
	CMeasurer *		pme = g_pols->_pme;
	COleObject *	pobj = pme->GetObjectFromCp(cp);
	Assert(pobj);

	ZeroMemory(&objdim, sizeof(objdim));

	pobj->MeasureObj(pme->_dvrInch, pme->_durInch, objdim.dur, objdim.heightsRef.dvAscent,
					 objdim.heightsRef.dvDescent, pcfmtin->lstxmRef.dvDescent, pme->GetTflow());
	
	pobj->MeasureObj(pme->_dvpInch, pme->_dupInch, dup, objdim.heightsPres.dvAscent,
					 objdim.heightsPres.dvDescent, pcfmtin->lstxmPres.dvDescent, pme->GetTflow());

	pobj->_plsdnTop = pcfmtin->plsdnTop;

	lserr = g_plsc->dnFinishRegular(1, pcfmtin->lsfrun.plsrun, pcfmtin->lsfrun.plschp, (PDOBJ)pobj, &objdim);
	if(lserrNone == lserr) 
	{
		lserr = g_plsc->dnSetRigidDup(pcfmtin->plsdnTop, dup);
		if(lserrNone == lserr) 
		{
			*pfmres = fmtrCompletedRun;

			if (pcfmtin->lsfgi.urPen + objdim.dur > pcfmtin->lsfgi.urColumnMax 
				&& !pcfmtin->lsfgi.fFirstOnLine)
			{
				*pfmres = fmtrExceededMargin;
			}
		}
	}
	return lserr;
}


/*
 *	OlsOleTruncateChunk(plocchnk, posichnk)
 *
 *	@func
 *		Truncate chunk plocchnk at the point posichnk
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsOleTruncateChunk(
	PCLOCCHNK plocchnk,		// (IN): locchnk to truncate
	PPOSICHNK posichnk)		// (OUT): truncation point
{
	LSERR			lserr;
	OBJDIM			objdim;
	PLSCHNK 		plschnk = plocchnk->plschnk;
	COleObject *	pobj;
	long			ur	 = plocchnk->lsfgi.urPen;
	long			urColumnMax = plocchnk->lsfgi.urColumnMax;

	for(DWORD i = 0; ur <= urColumnMax; i++)
	{
		AssertSz(i < plocchnk->clschnk,	"OlsOleTruncateChunk: exceeded group of chunks");

		pobj = (COleObject *)plschnk[i].pdobj;
		Assert(pobj);

		lserr = g_plsc->dnQueryObjDimRange(pobj->_plsdnTop, pobj->_plsdnTop, &objdim);
		if(lserr != lserrNone)
			return lserr;

		ur += objdim.dur;
	}
	posichnk->ichnk = i - 1;
	posichnk->dcp	= 1;
	return lserrNone;
}
/*
 *	OlsOleFindPrevBreakChunk(plocchnk, pposichnk, brkcond, pbrkout)
 *
 *	@func
 *		Find previous break in chunk
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsOleFindPrevBreakChunk(
	PCLOCCHNK	plocchnk, 
	PCPOSICHNK	pposichnk, 
	BRKCOND		brkcond,	//(IN): recommendation about break after chunk
	PBRKOUT		pbrkout)
{
	ZeroMemory(pbrkout, sizeof(*pbrkout));

	if (pposichnk->ichnk == ichnkOutside && (brkcond == brkcondPlease || brkcond == brkcondCan))
		{
		pbrkout->fSuccessful = fTrue;
		pbrkout->posichnk.ichnk = plocchnk->clschnk - 1;
		pbrkout->posichnk.dcp = plocchnk->plschnk[plocchnk->clschnk - 1].dcp;
		COleObject *pobj = (COleObject *)plocchnk->plschnk[plocchnk->clschnk - 1].pdobj;
		Assert(pobj);

		g_plsc->dnQueryObjDimRange(pobj->_plsdnTop, pobj->_plsdnTop, &pbrkout->objdim);
	}
	else
		pbrkout->brkcond = brkcondPlease;

	return lserrNone;
}


/*
 *	OlsOleForceBreakChunk(plocchnk, pposichnk, pbrkout)
 *
 *	@func
 *		Called when forced to break a line.
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsOleForceBreakChunk(
	PCLOCCHNK	plocchnk, 
	PCPOSICHNK	pposichnk, 
	PBRKOUT		pbrkout)
{
	ZeroMemory(pbrkout, sizeof(*pbrkout));
	pbrkout->fSuccessful = fTrue;

	if (plocchnk->lsfgi.fFirstOnLine && pposichnk->ichnk == 0 || pposichnk->ichnk == ichnkOutside)
		{
		pbrkout->posichnk.dcp = 1;
		COleObject *pobj = (COleObject *)plocchnk->plschnk[0].pdobj;
		Assert(pobj);

		g_plsc->dnQueryObjDimRange(pobj->_plsdnTop, pobj->_plsdnTop, &pbrkout->objdim);
		}
	else
		{
		pbrkout->posichnk.ichnk = pposichnk->ichnk;
		pbrkout->posichnk.dcp = 0;
		}

	return lserrNone;
}

/*
 *	OlsOleSetBreak(pdobj, brkkind, nBreakRecord, rgBreakRecord, nActualBreakRecord)
 *
 *	@func
 *		Set break
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsOleSetBreak(
	 PDOBJ pdobj,				// (IN): dobj which is broken
	 BRKKIND  brkkind,			// (IN): Previous/Next/Force/Imposed was chosen
	 DWORD	nBreakRecord,		// (IN): size of array
	 BREAKREC* rgBreakRecord,	// (OUT): array of break records
	 DWORD* nActualBreakRecord)	// (OUT): actual number of used elements in array
{
	return lserrNone;
}

LSERR WINAPI OlsOleGetSpecialEffectsInside(
	PDOBJ pdobj,			// (IN): dobj
	UINT *pEffectsFlags)	// (OUT): Special effects for this object
{
	*pEffectsFlags = 0;
	return lserrNone;
}

LSERR WINAPI OlsOleCalcPresentation(
	PDOBJ,					// (IN): dobj
	long,					// (IN): dup of dobj
	LSKJUST,				// (IN): LSKJUST
	BOOL fLastVisibleOnLine)// (IN): this object is last visible object on line
{
	return lserrNone;
}

/*
 *	OlsOleQueryPointPcp(pdobj, ppointuvQuery, plsqin, plsqout)
 *
 *	@func
 *		Query Ole object PointFromCp.
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsOleQueryPointPcp(
	PDOBJ	  pdobj,			//(IN): dobj to query
	PCPOINTUV ppointuvQuery,	//(IN): query point (uQuery,vQuery)
    PCLSQIN	  plsqin,			//(IN): query input
    PLSQOUT	  plsqout)			//(OUT): query output
{
	ZeroMemory(plsqout, sizeof(LSQOUT));

	plsqout->heightsPresObj = plsqin->heightsPresRun;
	plsqout->dupObj = plsqin->dupRun;
	return lserrNone;
}
	
/*
 *	OlsOleQueryCpPpoint(pdobj, dcp, plsqin, plsqout)
 *
 *	@func
 *		Query Ole object CpFromPoint.
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsOleQueryCpPpoint(
	PDOBJ	pdobj,		//(IN): dobj to query
	LSDCP	dcp,		//(IN):  dcp for query
    PCLSQIN	plsqin,		//(IN): query input
    PLSQOUT	plsqout)	//(OUT): query output
{
	ZeroMemory(plsqout, sizeof(LSQOUT));

	plsqout->heightsPresObj = plsqin->heightsPresRun;
	plsqout->dupObj = plsqin->dupRun;
	return lserrNone;
}

/*
 *	OlsOleDisplay(pdobj, pcdispin)
 *
 *	@func
 *		Display object
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsOleDisplay(
	PDOBJ	 pdobj,			//(IN): dobj to query
	PCDISPIN pcdispin)		//(IN): display info
{
	COleObject *pobj = (COleObject *)pdobj;
	Assert(pobj);

	CRenderer  *pre = g_pols->GetRenderer();
	const CDisplay *pdp = pre->GetPdp();
	
	POINTUV ptuv = {pcdispin->ptPen.x, pcdispin->ptPen.y - pre->GetLine().GetDescent()};

	if (pcdispin->lstflow == lstflowWS)
		ptuv.u -= pcdispin->dup - 1;

	pre->SetSelected(pcdispin->plsrun->IsSelected());
	pre->Check_pccs();
	pre->SetFontAndColor(pcdispin->plsrun->_pCF);

	if (pre->_fEraseOnFirstDraw)
		pre->EraseLine();

	pre->SetCurPoint(ptuv);
	pre->SetClipLeftRight(pcdispin->dup);

	if (!pobj->FWrapTextAround())
	{
		COls			*polsOld = g_pols;
		CLineServices	*plscOld = g_plsc;
		BOOL			fOLSBusyOld = g_OLSBusy;

		BOOL	fRestore = FALSE;

		if (g_plsc && g_pols)
		{
			// This is to fix a re-entrance problem.  
			// We first NULL out the two globals.  If the OleObject is using Richedit, it will
			// create a new LineService context.  By the time it get back to here, we will free that
			// context and restore the current context.  This is necessary since LineService will returns
			// error when we are using the same context in the Parent and then in the Ole Object using RE.
			g_plsc = NULL;
			g_pols = NULL;
			g_OLSBusy = FALSE;
			fRestore = TRUE;
		}
		pobj->DrawObj(pdp, pre->_dvpInch, pre->_dupInch, pre->GetDC(), &pre->GetClipRect(), pdp->IsMetafile(), 
					 &ptuv, pcdispin->ptPen.y - ptuv.v, pre->GetLine().GetDescent(), pre->GetTflow());

		if (fRestore)
		{
			// Time to delete the new context created within the DrawObject.
			if (g_pols)
				delete g_pols;

			// Restore old globals
			g_pols = polsOld;
			g_plsc = plscOld;
			g_OLSBusy = fOLSBusyOld;
		}
	}
	return lserrNone;
}

/*
 *	OlsOleDistroyDObj(pdobj)
 *
 *	@func
 *		Destroy object: nothing to do since object is destroyed elsewhere
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsOleDestroyDObj(
	PDOBJ pdobj)
{
	return lserrNone;
}


extern const LSIMETHODS vlsimethodsOle =
{
	OlsOleCreateILSObj,
	OlsOleDestroyILSObj,
    OlsOleSetDoc,
    OlsOleCreateLNObj,
    OlsOleDestroyLNObj,
	OlsOleFmt,
	0,//OlsOleFmtResume
	0,//OlsOleGetModWidthPrecedingChar
	0,//OlsOleGetModWidthFollowingChar
    OlsOleTruncateChunk,
    OlsOleFindPrevBreakChunk,
    0,//OlsOleFindNextBreakChunk
    OlsOleForceBreakChunk,
    OlsOleSetBreak,
	OlsOleGetSpecialEffectsInside,
	0,//OlsOleFExpandWithPrecedingChar
	0,//OlsOleFExpandWithFollowingChar
	OlsOleCalcPresentation,
	OlsOleQueryPointPcp,
	OlsOleQueryCpPpoint,
	0,//pfnEnum
    OlsOleDisplay,
    OlsOleDestroyDObj
};
#endif		// NOLINESERVICES