/*--------------------------------------------------------------------*//*:Ignore this sentence.
Copyright (C) 1999 - 2008 SIL International. All rights reserved.

Distributable under the terms of either the Common Public License or the
GNU Lesser General Public License, as specified in the LICENSING.txt file.

File: GrSlotState.h
Responsibility: Sharon Correll
Last reviewed: Not yet.

Copyright (C) 1999 by SIL International. All rights reserved.

Description:
    Classes GrSlotAbstract, GrSlotState, and GrSlotOutput
----------------------------------------------------------------------------------------------*/
#ifdef _MSC_VER
#pragma once
#endif
#ifndef SLOTSTATE_INCLUDED
#define SLOTSTATE_INCLUDED

namespace gr
{

class GrSlotAbstract;
class GrSlotState;
class GrSlotOutput;
class GrSlotStream;
class Font;

//:End Ignore


/*----------------------------------------------------------------------------------------------
	Subsumes GrSlotState and GrSlotOutput, both of which represent a single glyph
	and its slot attributes and features.
	
	Hungarian: slab
----------------------------------------------------------------------------------------------*/
class GrSlotAbstract
{
	friend class GrSlotStream;
	friend class GlyphInfo;

public:
	//	Constructor:
	GrSlotAbstract()
	{
	}

	~GrSlotAbstract()
	{
		// the table manager is responsible for destroying the contents of m_prgnVarLenBuf
	}

	void BasicInitializeAbstract(int cnCompPerLig, u_intslot * pnBuf)
	{
		m_dirc = kNotYetSet8;
		m_lb = kNotYetSet8;
		m_fInsertBefore = true;
		m_nDirLevel = -1;

		m_bIsSpace = -1; // unknown

		m_fInsertBefore = true;

		m_mMeasureSol = 0;
		m_mMeasureEol = 0;

		m_mJStretch0 = 0;
		m_mJShrink0 = 0;
		m_mJStep0 = 0;
		m_nJWeight0 = 0;
		m_mJWidth0 = 0;

		m_cnCompPerLig = byte(cnCompPerLig);
		m_prgnVarLenBuf = pnBuf;
	}
	
	void CopyAbstractFrom(GrSlotState * pslot);

	void SetBufferPtr(u_intslot * pn)
	{
		m_prgnVarLenBuf = pn;
	}

	gid16 GlyphID()				{ return m_chwGlyphID; }
	gid16 RawActualGlyph()		{ return m_chwActual; }
	float GetGlyphMetric(Font * pfont, int nGlyphMetricID, gid16 chwGlyphID);

	//GrSlotState * CompRefSlot(int i);

	int BreakWeight()
	{
		return (int)m_lb;
	}
	DirCode Directionality()
	{
		return DirCode(int(m_dirc));
	}
	int InsertBefore()
	{
		return m_fInsertBefore;
	}
	int MeasureSol()
	{
		return m_mMeasureSol;
	}
	int MeasureEol()
	{
		return m_mMeasureEol;
	}

	int DirLevel()
	{
		return m_nDirLevel;
	}

	int SpecialSlotFlag()
	{
		return m_spsl;
	}
	void SetSpecialSlotFlag(int spsl)
	{
		m_spsl = sdata8(spsl);
	}

	enum {
		kNotYetSet = 0x7FFF,
		kNotYetSet8 = 0x7F,
		kInvalidGlyph = 0xFFFF
	};

	gid16 ActualGlyphForOutput(GrTableManager * ptman);

	// Needed for GlyphInfo:
	float XPosition()
	{
		return m_xsPositionX;
	}
	float YPosition()
	{
		return m_ysPositionY;	// relative to baseline (positive is up)
	}

protected:
	gid16 m_chwGlyphID;
	gid16 m_chwActual;		// actual glyph to output (which is a different glyph for pseudos)

	sdata8 m_spsl;			// special slot flag: LB, bidi marker

//	bool m_fInitialLB;		// for LB slots: true if this is the initial LB;
							// false if it is the terminating LB
							// TODO: remove

	sdata8 m_dirc;			// default = kNotYetSet8 (read from glyph attr)
	sdata8 m_lb;			// default = kNotYetSet8 (read from glyph attr)

	sdata8 m_nDirLevel;

	// Slot attributes that are used by GrSlotOutput:
	short	m_mMeasureSol;
	short	m_mMeasureEol;

	unsigned short	m_mJStretch0;
	unsigned short	m_mJShrink0;
	unsigned short	m_mJStep0;
	int	m_mJWidth0;
	byte m_nJWeight0;

	bool m_fInsertBefore;	// default = true

	sdata8 m_bIsSpace;		// 0 = false, 1 = true, -1 = unknown

	byte m_cnCompPerLig;
	// There is a large block managed by either the GrTableManager (for GrSlotState)
	// or the segment (for GrSlotOutput); this variable points at the sub-buffer for this
	// particular slot:
	u_intslot * m_prgnVarLenBuf;	

	float m_xsPositionX;
	float m_ysPositionY;

};	// end of class GrSlotAbstract


/*----------------------------------------------------------------------------------------------
	A GrSlotState represents one slot as modified by a pass in the table.
	Each time a slot state is modified, a new instance is created, copying the relevant
	information.

	Hungarian: slot

	Other hungarian:
		m - integer indicating glyph design units ("em-units")
		sr - integer indicating slot reference
----------------------------------------------------------------------------------------------*/
class GrSlotState : public GrSlotAbstract
{
	friend class GrSlotStream;
	friend class FontMemoryUsage;

public:
	enum { kNeutral = 99 };

	//	Constructors:
	GrSlotState()
		:	GrSlotAbstract(),
			m_pslotPrevState(NULL)
	{
		m_vpslotAssoc.clear();
		//m_fInitialLB = false; // TODO: remove
		m_spsl = kspslNone;
		m_fNeutralAssocs = false;
		m_dircProc = kdircUnknown;
		m_fDirProcessed = false;

		m_vdislotAttLeaves.clear();
		m_fAttachMod = false;
		m_fShiftMod = false;
		m_dislotRootFixed = 0;
		ZapCompositeMetrics();
	}

	~GrSlotState()
	{
	}

	void BasicInitialize(int cnUserDefn, int cnCompPerLig, int cnFeat, u_intslot * pnBuf)
	{
		BasicInitializeAbstract(cnCompPerLig, pnBuf);

		m_xysGlyphWidth = -1;
		m_xysFontAscent = -1;
		m_xysFontDescent = -1;

		m_mAdvanceX = kNotYetSet;
		m_mAdvanceY = kNotYetSet;
		m_mShiftX = 0;
		m_mShiftY = 0;

		m_fAdvXSet = false; // for transduction logging
		m_fAdvYSet = false;

		m_srAttachTo = 0;
		m_nAttachLevel = 0;

		m_mAttachAtX = kNotYetSet;
		m_mAttachAtY = 0;
		m_nAttachAtGpoint = kNotYetSet;
		m_mAttachAtXOffset = 0;
		m_mAttachAtYOffset = 0;
		m_mAttachWithX = kNotYetSet;
		m_mAttachWithY = 0;
		m_nAttachWithGpoint = kNotYetSet;
		m_mAttachWithXOffset = 0;
		m_mAttachWithYOffset = 0;

		m_islotPosPass = kNotYetSet;

		m_cnUserDefn = byte(cnUserDefn);
		m_cnFeat = byte(cnFeat);

		m_fHasComponents = false;
	}

	void Initialize(gid16 chw, GrEngine *, GrFeatureValues fval,
		int ipass, int ichwSegOffset, int nUnicode = -1);
	void Initialize(gid16 chw, GrEngine *, GrSlotState * pslotFeat,
		int ipass, int ichwSegOffset);
	void Initialize(gid16 chw, GrEngine *, GrSlotState * pslotFeat,
		int ipass);

	void InitializeFrom(GrSlotState * pslot, int ipass);
	void CopyFeaturesFrom(GrSlotState * pslotSrc);
	void FixAttachmentTree(GrSlotState * pslotOld);

	void CopyFrom(GrSlotState * pslot, bool fCopyEverything = true);

	//	General:
	int RawSegOffset()		{ return m_ichwSegOffset; }

	void SetGlyphID(gid16 chw)
	{
		m_chwGlyphID = chw;
		m_chwActual = kInvalidGlyph;
		m_xysGlyphWidth = -1; // indicate glyph metrics are invalid
		m_ipassFsmCol = -1;
		m_colFsm = -1;
	}

	int PosPassIndex()
	{
		return m_islotPosPass;
	}
	void SetPosPassIndex(int islot, bool fInputToPosPass1)
	{
		// If we're resetting it, it should be to the same value as before:
		Assert(fInputToPosPass1 || m_islotPosPass == kNotYetSet || m_islotPosPass == islot);
		m_islotPosPass = islot;
	}
	void IncPosPassIndex()
	{
		m_islotPosPass++;
	}
	void ZapPosPassIndex()
	{
		m_islotPosPass = kNotYetSet;
	}

	int StyleIndex()				{ return m_bStyleIndex; }

	int AttachTo()				{ return m_srAttachTo; }
	int AttachLevel()			{ return m_nAttachLevel; }

	int RawAttachAtX()			{ return m_mAttachAtX; }
	int AttachAtY()				{ return m_mAttachAtY; }
	int AttachAtGpoint()		{ return m_nAttachAtGpoint; }
	int AttachAtXOffset()		{ return m_mAttachAtXOffset; }
	int AttachAtYOffset()		{ return m_mAttachAtYOffset; }

	int RawAttachWithX()		{ return m_mAttachWithX; }
	int AttachWithY()			{ return m_mAttachWithY; }
	int AttachWithGpoint()		{ return m_nAttachWithGpoint; }
	int AttachWithXOffset()		{ return m_mAttachWithXOffset; }
	int AttachWithYOffset()		{ return m_mAttachWithYOffset; }

	void Associate(GrSlotState *);
	void Associate(GrSlotState *, GrSlotState *);
	void Associate(std::vector<GrSlotState*> &);
	void ClearAssocs();

	int AssocsSize()				{ return m_vpslotAssoc.size(); }
	GrSlotState * RawBeforeAssocSlot()
	{
		if (m_vpslotAssoc.size() == 0)
			return NULL;
		return m_vpslotAssoc[0];
	}
	GrSlotState * RawAfterAssocSlot()
	{
		if (m_vpslotAssoc.size() == 0)
			return NULL;
		return m_vpslotAssoc.back();
	}
	////GrSlotState * AssocSlot(int i)		{ return m_vpslotAssoc[i]; }

	GrSlotState * AssocSlot(int i)
	{
		if (i < 0)
			return NULL;
		if (i >= signed(m_vpslotAssoc.size()))
			return NULL;

		GrSlotState * pslotAssoc = m_vpslotAssoc[i];
		// handle possible reprocessing
		while (pslotAssoc && pslotAssoc->PassModified() == m_ipassModified)
			pslotAssoc = pslotAssoc->m_pslotPrevState;
		return pslotAssoc;
	}

	void AllAssocs(std::vector<int> & vichw);
	int BeforeAssoc();
	int AfterAssoc();

	void CleanUpAssocs();

	void SetComponentRefsFor(GrSlotOutput *, int iComp = -1);
	void AllComponentRefs(std::vector<int> & vichw);

	int PassModified()			{ return m_ipassModified; }
	int SegOffset();
	int UnderlyingPos();
	GrSlotState * PrevState()	{ return m_pslotPrevState; }

	void MarkDeleted()	// for now, do nothing
	{
	}

	int IsSpace(GrTableManager * ptman);

	// TODO: remove argument from these methods; it is no longer needed.
	bool IsLineBreak(gid16 chwLB)
	{
		return (IsInitialLineBreak(chwLB) || IsFinalLineBreak(chwLB));
		//return (m_chwGlyphID == chwLB); // TODO: remove
	}
	bool IsInitialLineBreak(gid16 chwLB)
	{
		return (m_spsl == kspslLbInitial);
		//return (IsLineBreak(chwLB) && m_fInitialLB == true); // TODO: remove
	}
	bool IsFinalLineBreak(gid16 chwLB)
	{
		return (m_spsl == kspslLbFinal);
		//return (IsLineBreak(chwLB) && m_fInitialLB == false); // TODO: remove
	}

	bool IsBidiMarker()
	{
		switch (m_spsl)
		{
		case kspslLRM:
		case kspslRLM:
		case kspslLRO:
		case kspslRLO:
		case kspslLRE:
		case kspslRLE:
		case kspslPDF:
			return true;
		default:
			return false;
		}
		return false;
	}

	bool HasComponents()	{ return m_fHasComponents; }

	// Directionality as determined by the bidi algorithm
	DirCode DirProcessed() // return the value
	{
		Assert(m_dirc != kNotYetSet8);
		if (m_dircProc == kdircUnknown)
			m_dircProc = DirCode(m_dirc);
		return m_dircProc;
	}
	void SetDirProcessed(DirCode dirc) // set the directionality
	{
		m_dircProc = dirc;
	}
	bool DirHasBeenProcessed() // has this slot been fully processed?
	{
		return m_fDirProcessed;
	}
	void MarkDirProcessed() // this slot has been fully processed
	{
		m_fDirProcessed = true;
	}

	int RawAdvanceX()			{ return m_mAdvanceX; }
	int RawAdvanceY()			{ return m_mAdvanceY; }
	int ShiftX()				{ return m_mShiftX; }
	int ShiftY()				{ return m_mShiftY; }

	//	Slot attributes that must be calculated:

	int AdvanceX(GrTableManager * ptman)
	{
		if (m_mAdvanceX == kNotYetSet)
			//	Initialize it from the glyph metric (adjusted for hinting).
			m_mAdvanceX = short(GlyphMetricEmUnits(ptman, kgmetAdvWidth));
		return m_mAdvanceX;
	}

	int AdvanceY(GrTableManager * ptman)
	{
		if (m_mAdvanceY == kNotYetSet)
			//	Initialize it from the glyph metric (adjusted for hinting).
			m_mAdvanceY = short(GlyphMetricEmUnits(ptman, kgmetAdvHeight));
		return m_mAdvanceY;
	}

	int AttachAtX(GrTableManager * ptman, GrSlotStream * psstrm)
	{
		if (m_mAttachAtX == kNotYetSet)
		{
			Assert(false);	// Should have already been set in HandleModifiedPosition,
							// but just in case...
			if (m_srAttachTo == 0)
				return 0;
			else
				m_mAttachAtX = short(AttachRoot(psstrm)->AdvanceX(ptman)); // attach on the right
		}
		return m_mAttachAtX;
	}

	int AttachWithX(GrTableManager * ptman, GrSlotStream * psstrm)
	{
		if (m_mAttachAtX == kNotYetSet)
		{
			Assert(false);	// Should have already been set in HandleModifiedPosition,
							// but just in case.
			if (!m_srAttachTo == 0)
				return 0;
			else
				m_mAttachAtX = 0; // attach on the right
		}
		return m_mAttachAtX;
	}

	int JStretch()
	{
		return m_mJStretch0;
	}
	int JShrink()
	{
		return m_mJShrink0;
	}
	int JStep()
	{
		return m_mJStep0;
	}
	int JWeight()
	{
		return m_nJWeight0;
	}
	int JWidth()
	{
		return m_mJWidth0;
	}

	//	Slot attribute setters:

	void SetAdvanceX(int mVal)
	{
		Assert(mVal < 0xFFFF); m_mAdvanceX = short(mVal & 0xFFFF); m_fShiftMod = true;
		m_fAdvXSet = true;	// for transduction logging
	}
	void SetAdvanceY(int mVal)
	{
		Assert(mVal < 0xFFFF); m_mAdvanceY = short(mVal & 0xFFFF); m_fShiftMod = true;
		m_fAdvYSet = true;	// for transduction logging
	}
	void SetShiftX(int mVal)
	{
		Assert(mVal < 0xFFFF); m_mShiftX = short(mVal & 0xFFFF);
		m_fShiftMod = true;
	}
	void SetShiftY(int mVal)
	{
		Assert(mVal < 0xFFFF); m_mShiftY = short(mVal & 0xFFFF);
		m_fShiftMod = true;
	}

	void SetAttachTo(int srVal)
	{
		Assert(srVal < 0xFFFF);
		m_srAttachTo = short(srVal & 0xFFFF);
		m_fAttachMod = true;
	}
	void SetAttachLevel(int nVal) {
		Assert(nVal < 0xFFFF); m_nAttachLevel = short(nVal & 0xFFFF); m_fAttachMod = true; }

	void SetAttachAtX(int mVal)
	{
		Assert(mVal < 0xFFFF);
		m_mAttachAtX = short(mVal) & 0xFFFF;
		m_fAttachMod = true;
	}
	void SetAttachAtY(int mVal) {
		Assert(mVal < 0xFFFF); m_mAttachAtY = short(mVal & 0xFFFF); m_fAttachMod = true; }
	void SetAttachAtGpoint(int nVal) {
		m_nAttachAtGpoint = short(nVal); m_fAttachMod = true; }
	void SetAttachAtXOffset(int mVal) {
		Assert(mVal < 0xFFFF); m_mAttachAtXOffset = short(mVal & 0xFFFF); m_fAttachMod = true; }
	void SetAttachAtYOffset(int mVal) {
		Assert(mVal < 0xFFFF); m_mAttachAtYOffset = short(mVal & 0xFFFF); m_fAttachMod = true; }

	void SetAttachWithX(int mVal)	{
		Assert(mVal < 0xFFFF); m_mAttachWithX = short(mVal & 0xFFFF); m_fAttachMod = true; }
	void SetAttachWithY(int mVal) {
		Assert(mVal < 0xFFFF); m_mAttachWithY = short(mVal & 0xFFFF); m_fAttachMod = true; }
	void SetAttachWithGpoint(int nVal) {
		m_nAttachWithGpoint = short(nVal); m_fAttachMod = true; }
	void SetAttachWithXOffset(int mVal) {
		Assert(mVal < 0xFFFF); m_mAttachWithXOffset = short(mVal & 0xFFFF); m_fAttachMod = true; }
	void SetAttachWithYOffset(int mVal) {
		Assert(mVal < 0xFFFF); m_mAttachWithYOffset = short(mVal & 0xFFFF); m_fAttachMod = true; }

	void SetCompRefSlot(GrTableManager * ptman, int i, GrSlotState * pslotComp);

	void SetBreakWeight(int lb)				{ m_lb = sdata8(lb); }
	void SetInsertBefore(bool f)			{ m_fInsertBefore = f; }

	void SetDirectionality(DirCode dirc)	{ m_dirc = sdata8(dirc); }
	void SetDirLevel(int n)					{ m_nDirLevel = sdata8(n); }

	void SetMeasureSol(int mVal)			{ m_mMeasureSol = short(mVal); }
	void SetMeasureEol(int mVal)			{ m_mMeasureEol = short(mVal); }

	void SetJStretch(int mVal)				{ m_mJStretch0 = short(mVal); }
	void SetJShrink(int mVal)				{ m_mJShrink0 = short(mVal); }
	void SetJStep(int mVal)					{ m_mJStep0 = short(mVal); }
	void SetJWeight(int nVal)				{ m_nJWeight0 = byte(nVal); }
	void SetJWidth(int mVal)				{ m_mJWidth0 = mVal; }
	void AddJWidthToAdvance(GrTableManager * ptman)
	{
		// Don't change m_fShiftMod.
		m_mAdvanceX = short(m_mJWidth0 + AdvanceX(ptman)); // make sure it is calculated
		m_mJWidth0 = 0;
		m_fAdvXSet = true;	// for transduction logging
	}

	int PostBidiDirLevel(GrTableManager * ptman);

	bool BaseEarlierInStream();

	void ZapDirLevel()
	{
		m_nDirLevel = -1;
		m_dircProc = kdircUnknown;
		m_fDirProcessed = false;
	}

	int GlyphAttrValueEmUnits(GrTableManager * ptman, int nAttrID);
	int GlyphMetricEmUnits(GrTableManager * ptman, int nGlyphMetricID);

	float GlyphAttrValueLogUnits(GrTableManager * ptman, int nAttrID);
	float GlyphMetricLogUnits(GrTableManager * ptman, int nGlyphMetricID);

//	void HandleModifiedCluster(GrTableManager * ptman,
//		GrSlotStream * psstrm, int islotThis);
	void HandleModifiedPosition(GrTableManager * ptman,
		GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islotThis);

	void CalcCompositeMetrics(GrTableManager * ptman, GrSlotStream * psstrm,
		int nLevel, bool fThorough = false);

	void Position(GrTableManager * ptman,
		GrSlotStream * psstrmOut, int * pmXPos, int * pmYPos);

	void ZapPosition()
	{
		m_xsPositionX = kNegInfFloat;
		m_ysPositionY = kNegInfFloat;
	}

	void SetXPos(float xs)
	{
		m_xsPositionX = xs;
	}
	void SetYPos(float ys)
	{
		m_ysPositionY = ys;	// relative to baseline (positive is up)
	}

	float ClusterRootOffset()	{ return -m_xsClusterXOffset; } // the offset of the root
																// relative to the whole cluster

	float ClusterAdvWidthFrom(float xs)	{ return xs + m_xsClusterAdv; }
	float ClusterBbLeftFrom(float xs)	{ return xs + m_xsClusterBbLeft; }
	float ClusterBbRightFrom(float xs)	{ return xs + m_xsClusterBbRight; }

	float ClusterLsb(GrSlotStream * psstrm, float xs)
	{
		return ClusterBbLeft(psstrm) + xs;
	}
	float ClusterRsb(GrSlotStream * psstrm, float xs)
	{
		return ClusterAdvWidthFrom(xs) - ClusterBbRightFrom(xs);
	}

	float ClusterAdvWidth(GrSlotStream * psstrm)
	{
		return ClusterAdvWidthFrom(Base(psstrm)->ClusterRootOffset());
	}
//	int ClusterAdvHeight(GrSlotStream * psstrm)
//	{
//		Assert(m_mAdvanceY != kNotYetSet);
//		return EmUnitsToTwips(m_mAdvanceY);
//	}
	float ClusterBbLeft(GrSlotStream * psstrm)
	{
		return ClusterBbLeftFrom(Base(psstrm)->ClusterRootOffset());
	}
	float ClusterBbRight(GrSlotStream * psstrm)
	{
		return ClusterBbRightFrom(Base(psstrm)->ClusterRootOffset());
	}
	float ClusterBbTop(GrSlotStream * psstrm)
	{
		return m_ysClusterBbTop;
	}
	float ClusterBbBottom(GrSlotStream * psstrm)
	{
		return m_ysClusterBbBottom;
	}
	float ClusterBbWidth(GrSlotStream * psstrm)
	{
		return m_xsClusterBbRight - m_xsClusterBbLeft + 1;
	}
	float ClusterBbHeight(GrSlotStream * psstrm)
	{
		return m_ysClusterBbTop - m_ysClusterBbBottom + 1;
	}
	float ClusterLsb(GrSlotStream * psstrm)
	{
		return ClusterBbLeft(psstrm);
	}
	float ClusterRsb(GrSlotStream * psstrm)
	{
		return ClusterAdvWidth(psstrm) - ClusterBbRight(psstrm);
	}

	float GlyphXOffset(GrSlotStream * psstrm, float fakeItalicRatio = 0);
	float GlyphYOffset(GrSlotStream * psstrm = NULL);

	bool IsBase()
	{
		Assert(m_dislotRootFixed == m_srAttachTo);
		return (m_dislotRootFixed == 0);
	}

	bool HasClusterMembers()
	{
		return (m_vdislotAttLeaves.size() > 0);
	}

	GrSlotState * AttachRoot(GrSlotStream * psstrm)
	{
		if (m_srAttachTo == 0)
			return NULL;
		else
			return SlotAtOffset(psstrm, m_srAttachTo);
	}

	int AttachRootPosPassIndex()
	{
		return PosPassIndex() + m_srAttachTo;
	}

	GrSlotState * Base(GrSlotStream * psstrm)
	{
		GrSlotState * pslotRoot = AttachRoot(psstrm);
		if (!pslotRoot)
			return this;
		else
			return pslotRoot->Base(psstrm);
	}

	int LastLeafOffset(GrSlotStream * psstrm);

	void AddLeaf(int dislot)
	{
		m_vdislotAttLeaves.push_back(dislot * -1);
	}
	void RemoveLeaf(int dislot)
	{
		for (size_t iislot = 0; iislot < m_vdislotAttLeaves.size(); iislot++)
		{
			if (m_vdislotAttLeaves[iislot] == dislot * -1)
			{
				m_vdislotAttLeaves.erase(m_vdislotAttLeaves.begin() + iislot);
				return;
			}
		}
		Assert(false);
	}

	bool HasAsRoot(GrSlotStream * psstrm, GrSlotState * pslot)
	{
		GrSlotState * pslotRoot = AttachRoot(psstrm);
		if (pslotRoot == pslot)
			return true;
		else if (pslotRoot == NULL)
			return false;
		else
			return pslotRoot->HasAsRoot(psstrm, pslot);
	}

	bool HasAsPreviousState(GrSlotState * pslot)
	{
		if (this == pslot)
			return true;
		else
			return m_pslotPrevState->HasAsPreviousState(pslot);
	}

	void EnsureCacheForOutput(GrTableManager * ptman);

	bool IsLrm();
	bool IsRlm();

	//	Cache of FSM column information for the most recent pass:
	int FsmColumn() { return m_colFsm; }
	int PassNumberForColumn() { return m_ipassFsmCol; }
	void CacheFsmColumn(int ipass, int col)
	{
		m_colFsm = col;
		m_ipassFsmCol = ipass;
	}

	//	Variable-length buffer--includes four sub-buffers:
	//	* user-defined variables
	//	* component.???.ref assignments
	//	* mapping from components used to global attribute IDs for components
	//	* feature values

	// user-defined slot attributes
	int UserDefn(int slati)
	{
		Assert(slati < m_cnUserDefn);
		return m_prgnVarLenBuf[slati].nValue;
	}
	void SetUserDefn(int slati, int nVal)
	{
		Assert(slati < m_cnUserDefn);
		m_prgnVarLenBuf[slati].nValue = nVal;
	}
	u_intslot * PUserDefnBuf()
	{
		return m_prgnVarLenBuf;
	}

	// pointer to the associated slot which is the value of the comp.ref attribute
	GrSlotState * CompRef(int slati)
	{
		Assert(slati < m_cnCompPerLig);
		return m_prgnVarLenBuf[m_cnUserDefn + slati].pslot;
	}
	void SetCompRef(int slati, GrSlotState * pvSlot)
	{
		Assert(slati < m_cnCompPerLig);
		m_prgnVarLenBuf[m_cnUserDefn + slati].pslot = pvSlot;
	}
	u_intslot * PCompRefBuf()
	{
		return m_prgnVarLenBuf + m_cnUserDefn;
	}

	// global component identifier
	int Slati(int i)
	{
		Assert(i < m_cnCompPerLig);
		return m_prgnVarLenBuf[m_cnUserDefn + m_cnCompPerLig + i].nValue;
	}
	void SetSlati(int i, int n)
	{
		Assert(i < m_cnCompPerLig);
		m_prgnVarLenBuf[m_cnUserDefn + m_cnCompPerLig + i].nValue = n;
	}
	u_intslot * PSlatiBuf()
	{
		return m_prgnVarLenBuf + m_cnUserDefn + m_cnCompPerLig;
	}

	// feature settings
	int FeatureValue(int i)
	{
		Assert(i < m_cnFeat);
		return m_prgnVarLenBuf[m_cnUserDefn + (m_cnCompPerLig * 2) + i].nValue;
	}

	u_intslot * PFeatureBuf()
	{
		return m_prgnVarLenBuf + m_cnUserDefn + (m_cnCompPerLig * 2);
	}

	int CExtraSpace()
	{
		return m_cnUserDefn + (m_cnCompPerLig * 2) + m_cnFeat;
	}

	GrSlotState * CompRefSlot(int i);

	void GetFeatureValues(GrFeatureValues * pfval)
	{
		pfval->m_nStyleIndex = m_bStyleIndex;
		std::fill(pfval->m_rgnFValues, pfval->m_rgnFValues + kMaxFeatures, 0);
		for (size_t i = 0; i < m_cnFeat; i++)
			pfval->m_rgnFValues[i] = PFeatureBuf()[i].nValue;
	}

	//	For transduction logging:
#ifdef TRACING
	void SlotAttrsModified(bool * rgfMods, bool fPreJust, int * pccomp, int * pcassoc);
	void LogSlotAttributeValue(GrTableManager *, std::ostream &, int ipass, int slat, int icomp,
		bool fPreJust, bool fPostJust);
	void LogAssociation(GrTableManager * ptman,
		std::ostream & strmOut, int ipass, int iassoc, bool fBoth, bool fAfter);
	void LogXmlAttributes(std::ostream & strmOut, GrTableManager * ptman, GrSlotStream * psstrmOut,
		int ipass, int islot,
		bool fPreJust, bool fPostJust, bool fBidi, bool fBidiNext, int nIndent);
	int GetSlotAttrValue(std::ostream & strmOut, GrTableManager * ptman,
		int ipass, int slat, int iIndex, bool fPreJust, bool fPostJust);
	int m_islotTmpIn;		// for use by transduction log; index of slot in input stream
	int m_islotTmpOut;		// ditto; index of slot in output stream
#endif // TRACING

protected:

	//	Instance variables:
	int m_ipassModified;	// pass in which this slot was modified

	GrSlotState * m_pslotPrevState;

	int	m_ichwSegOffset;	// for original (pass 0) slot states: position in
							// underlying text relative to the official
							// beginning of the segment;
							// should == kInvalid for other slot states
	int m_islotPosPass;		// index of slot in positioning streams, relative to first official
							// slot in the segment (possibly the LB slot)

	int m_colFsm;			// which FSM column this glyph corresponds to...
	int m_ipassFsmCol;		// ...for the most recent pass

	std::vector<GrSlotState*> m_vpslotAssoc;	// association mappings

	bool m_fNeutralAssocs;		// true if we've set the associations to some neutral
								// default, rather than them being set explicitly
								// within a rule

	int m_nUnicode; // for debugging

	DirCode m_dircProc;		// directionality as processed in bidi algorithm
	bool m_fDirProcessed;

	//	affects length of variable-length buffer
	byte m_cnUserDefn;
	byte m_cnFeat;

	byte m_bStyleIndex;

	// Slot attributes:
	short	m_mAdvanceX;
	short	m_mAdvanceY;
	short	m_mShiftX;
	short	m_mShiftY;

	short	m_srAttachTo;
	short	m_nAttachLevel;

	short	m_mAttachAtX;
	short	m_mAttachAtY;
	short	m_mAttachAtXOffset;
	short	m_mAttachAtYOffset;
	short	m_mAttachWithX;
	short	m_mAttachWithY;
	short	m_mAttachWithXOffset;
	short	m_mAttachWithYOffset;

	short	m_nAttachAtGpoint;
	short	m_nAttachWithGpoint;

	// Raw glyph metrics (directly from font)
	float m_xysFontAscent;
	float m_xysFontDescent;
	float m_xysGlyphWidth;
	float m_xysGlyphHeight;
	float m_xysGlyphX;
	float m_xysGlyphY;
	float m_xysAdvX;
	float m_xysAdvY; 

	//	Attachment and metrics

	//	This is a flag that is set whenever we change the value of any of the attach
	//	slot attributes. Then when we want to find out something about the attachments,
	//	if it is set, we have some work to do in updating the pointers and metrics.
	bool m_fAttachMod;

	//	This is a flag that is set whenever we change the value of any of the shift or
	//	advance slot attributes. It forces us to zap the metrics.
	bool m_fShiftMod;

	//	This glyph (and all its leaves, if any) have a zero advance width;
	//	never allow it to affect the advance width of a cluster it is part of.
	bool m_fIgnoreAdvance;

	int m_dislotRootFixed;	// the offset of the slot (relative to this one) that considers
							// this slot to be one of its leaves
	std::vector<int> m_vdislotAttLeaves;

	//	The following are used by the CalcCompositeMetrics() method and depend on
	//	the cluster level that was passed as an argument.

	int m_nCompositeLevel;	// cluster level last used to calculate composite metrics;
							// kNegInfinity if uncalculated

	//	offsets for this node only, relative to cluster base
	float m_xsOffsetX;
	float m_ysOffsetY; // relative to baseline (positive is up)

	//	offsets for this node and its leaves, relative to cluster base (y-coords are
	//	relative to baseline)
	float m_xsClusterXOffset;
	float m_xsClusterAdv;
	float m_xsClusterBbLeft;
	float m_xsClusterBbRight;
	float m_ysClusterBbTop;
	float m_ysClusterBbBottom;

	//	cumulative total of shifts for this node and roots; advance needs to ignore these
	float m_xsRootShiftX;
	float m_ysRootShiftY;

	bool m_fHasComponents;	// default = false

	//	Private methods:

	void CopyAbstractFrom(GrSlotState * pslot);

	void AdjustRootMetrics(GrTableManager * ptman, GrSlotStream *);
	void InitMetrics(GrTableManager * ptman, GrSlotState * pslotRoot);
	void InitLeafMetrics(GrTableManager * ptman, GrSlotState * pslotRoot);
	void InitRootMetrics(GrTableManager * ptman);
	void CalcRootMetrics(GrTableManager * ptman, GrSlotStream *, int nLevel);
	void AttachToRoot(GrTableManager * ptman, GrSlotStream *, GrSlotState * pslotNewRoot);
	void AttachLogUnits(GrTableManager * ptman,
		GrSlotState * pslotRoot,
		float * pxsAttAtX, float * pysAttAtY,
		float * pxsAttWithX, float * pysAttWithY);

	GrSlotState * SlotAtOffset(GrSlotStream * psstrm, int dislot);	// ENHANCE SharonC: inline?

	void EnsureLocalAttachmentTree(GrTableManager * ptman,
		GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islotThis);

	void ZapCompositeMetrics()
	{
		m_nCompositeLevel = kNegInfinity;
		m_xsPositionX = kNegInfFloat;
		m_ysPositionY = kNegInfFloat;
		m_xsOffsetX = 0;
		m_ysOffsetY = 0;
		m_xsRootShiftX = 0;
		m_ysRootShiftY = 0;
		ZapRootMetrics();
	}

	void ZapRootMetrics()
	{
		m_xsClusterXOffset = 0;
		m_xsClusterAdv = 0;
		m_xsClusterBbLeft = 0;
		m_xsClusterBbRight = 0;
		m_ysClusterBbTop = 0;
		m_ysClusterBbBottom = 0;
		m_fIgnoreAdvance = false;
	}

	void ZapMetricsAndPositionDownToBase(GrSlotStream * psstrm)
	{
		ZapCompositeMetrics();
		if (m_dislotRootFixed)
			SlotAtOffset(psstrm, m_dislotRootFixed)->ZapMetricsAndPositionDownToBase(psstrm);
	}

	void ZapMetricsOfLeaves(GrSlotStream * psstrm, bool fThis = false)
	{
		if (fThis)
			ZapCompositeMetrics();
		for (size_t islot = 0; islot < m_vdislotAttLeaves.size(); islot++)
		{
			SlotAtOffset(psstrm, m_vdislotAttLeaves[islot])->ZapMetricsOfLeaves(psstrm, true);
		}
	}

public: 	// for transduction logging
	bool m_fAdvXSet;
	bool m_fAdvYSet;

};	// end of class GrSlotState


/*----------------------------------------------------------------------------------------------
	A GrSlotOutput represents one slot as the final output of the final pass. These
	are recorded in the segment.

	Hungarian: slout
----------------------------------------------------------------------------------------------*/
class GrSlotOutput : public GrSlotAbstract
{
	friend class GlyphInfo;
	friend class Segment;
	friend class SegmentMemoryUsage;

public:
	GrSlotOutput()
	{
		m_cComponents = 0;
		m_isloutClusterBase = -1;	// not part of any cluster
		m_disloutCluster = 0;
		m_igbb = -1;
	}

	void ExactCopyFrom(GrSlotOutput * pslout, u_intslot * pnVarLenBuf, int cnExtraPerSlot);

	void InitializeOutputFrom(GrSlotState * pslot);

	int BeforeAssoc()
	{
		return m_ichwBeforeAssoc;	// relative to the official beginning of the segment
	}
	int AfterAssoc()
	{
		return m_ichwAfterAssoc;	// relative to the official beginning of the segment
	}

	void SetBeforeAssoc(int ichw)
	{
		m_ichwBeforeAssoc = ichw;
	}
	void SetAfterAssoc(int ichw)
	{
		m_ichwAfterAssoc = ichw;
	}
	void AddComponentReference(int ichw, int slati)
	{
		if (m_cComponents >= m_cnCompPerLig)
		{
			Assert(false);	// ignore the requested component ref
		}
		else
		{
			m_prgnVarLenBuf[m_cComponents].nValue
				= ichw;

			// OBSOLETE comment:
			//	Maps the used components to the defined components. Normally this will be
			//	one-to-one and the buffer will hold [0,1,2...]. But possibly we may
			//	have defined components a, b, and c, but only mapped a and c to actual
			//	characters. This buffer will then hold [0,2].

			m_prgnVarLenBuf[m_cnCompPerLig + m_cComponents].nValue
				= slati;

//			Assert(iComp >= m_cComponents); // because we process them in order they are defined
											// in, but we could have skipped some that are
											// defined
			m_cComponents++;
		}
	}
	int NumberOfComponents() // the number used by the rules
	{
		return m_cComponents;
	}
	//	Index of ligature components, relative to the beginning of the segment.
	//	iComp is index of components USED in this glyph.
	int UnderlyingComponent(int iComp)
	{
		Assert(iComp < m_cnCompPerLig);
		return m_prgnVarLenBuf[iComp].nValue;
	}
	int ComponentId(int iComp)
	{
		return m_prgnVarLenBuf[m_cnCompPerLig + iComp].nValue;
	}

	//u_intslot * PCompRefBufSlout()
	//{
	//	return m_prgnVarLenBuf;
	//}

	int CExtraSpaceSlout()
	{
		return (m_cnCompPerLig * 2);
	}

	void SetClusterBase(int islout)
	{
		m_isloutClusterBase = islout;
	}

	int ClusterBase()
	{
		return m_isloutClusterBase;
	}

	bool IsPartOfCluster()
	{
		return (m_isloutClusterBase > -1);
	}

	//int NumClusterMembers()
	//{
	//	return m_visloutClusterMembers.size();
	//}
	//int ClusterMember(int iislout)
	//{
	//	return m_visloutClusterMembers[iislout];
	//}

	void AddClusterMember(int isloutThis, int isloutAttached)
	{
		m_disloutCluster = sdata8(max(int(m_disloutCluster), abs(isloutThis - isloutAttached)));
	}

	void ClusterMembers(Segment * pseg, int islout, std::vector<int> & visloutRet);

	int ClusterRange()
	{
		return m_disloutCluster;
	}

	float ClusterXOffset()	{ return m_xsClusterXOffset; }
	float ClusterAdvance()	{ return m_xsClusterAdvance; }
	void SetClusterXOffset(float xs)	{ m_xsClusterXOffset = xs; }
	void SetClusterAdvance(float xs)	{ m_xsClusterAdvance = xs; }

	float GlyphMetricLogUnits(Font * pfont, int nGlyphMetric);
	//float GlyphMetricLogUnits(int nMetricID);

	int GlyphBbIndex()				{ return m_igbb; }
	void SetGlyphBbIndex (int i)	{ m_igbb = i; }

	Rect BoundingBox(Font & font)
	{
		Rect rectBB;
		rectBB.left = m_xsPositionX + GlyphMetricLogUnits(&font, kgmetBbLeft);
		if (IsSpace())
			rectBB.right = m_xsPositionX + GlyphMetricLogUnits(&font, kgmetAdvWidth);
		else
			rectBB.right = m_xsPositionX + GlyphMetricLogUnits(&font, kgmetBbRight);
		rectBB.top = m_ysPositionY + GlyphMetricLogUnits(&font, kgmetBbTop);
		rectBB.bottom = m_ysPositionY + GlyphMetricLogUnits(&font, kgmetBbBottom);
		return rectBB;
	}

	bool IsLineBreak()
	{
		return (IsInitialLineBreak() || IsFinalLineBreak());
	}
	bool IsInitialLineBreak()
	{
		return (m_spsl == kspslLbInitial);
	}
	bool IsFinalLineBreak()
	{
		return (m_spsl == kspslLbFinal);
	}

	//float AdvanceXMetric()
	//{
	//	return m_xysAdvX;
	//}

	bool IsSpace();

	void AdjustPosXBy(float dxs)
	{
		m_xsPositionX += dxs;
		//m_rectBB.left += dxs;
		//m_rectBB.right += dxs;
	}

    void ShiftForDirDepthChange(float dxsSegWidth);

	// Used by GlyphInfo
	int IndexAttachedTo();
	//inline float AdvanceX(Segment * pseg);
	//inline float AdvanceY(Segment * pseg);
	float MaxStretch(Segment * pseg, int level);
	float MaxShrink(Segment * pseg, int level);
	float StretchStep(Segment * pseg, int level);
	int JustWeight(int level);
	float JustWidth(Segment * pseg, int level);
	float MeasureSolLogUnits(Segment * pseg);
	float MeasureEolLogUnits(Segment * pseg);
	Rect ComponentRect(Segment * pseg, int icomp);

protected:
	//	Instance variables:

	sdata8 m_cComponents;

	data8 m_disloutCluster;		// how far to search on either side of this glyph to find
								// other members of the cluster;
								// 0 means there are no cluster members or this glyph is
								// attached to some other base

	int m_isloutClusterBase;	// the index of the slot that serves as the base for the
								// cluster this slot is a part of; -1 if not part of cluster

	int	m_ichwBeforeAssoc;		// index of associated character in the string
	int	m_ichwAfterAssoc;		// (relative to the official beginning of the segment)
								// char might possibly not be officially in this segment,
								// in which case value is infinity

	// Measurements for highlighting an entire cluster, relative to origin of this slot,
	// which is the cluster base.
	float m_xsClusterXOffset;
	float m_xsClusterAdvance;	// for single non-cluster glyphs, advance width of positioned glyph

	// Index into m_prggbb in Segment; -1 indicates a line break slot that is not rendered:
	int m_igbb;

	float m_xsAdvanceX;
//	float m_ysAdvanceY;	-- not used
//	Rect m_rectBB;

};	// end of class GrSlotOutput

} // namespace gr


#endif // !SLOTSTATE_INCLUDED
