/*
 * %W% %E%
 *
 * (C) Copyright IBM Corp. 1998-2003 - All Rights Reserved
 *
 */

#include "LETypes.h"
#include "OpenTypeTables.h"
#include "GlyphDefinitionTables.h"
#include "GlyphPositionAdjustments.h"
#include "GlyphIterator.h"
#include "Lookups.h"
#include "LESwaps.h"

U_NAMESPACE_BEGIN

const char InsertionList::fgClassID = 0;

InsertionList::InsertionList(le_bool rightToLeft)
: head(NULL), tail(NULL), growAmount(0), append(rightToLeft)
{
	tail = (InsertionRecord *) &head;
}

InsertionList::~InsertionList()
{
	reset();
}

void InsertionList::reset()
{
	while (head != NULL) {
		InsertionRecord *record = head;

		head = head->next;
		LE_DELETE_ARRAY(record);
	}

	tail = (InsertionRecord *) &head;
	growAmount = 0;
}

le_int32 InsertionList::getGrowAmount()
{
	return growAmount;
}

LEGlyphID *InsertionList::insert(le_int32 position, le_int32 count)
{
	InsertionRecord *insertion = (InsertionRecord *) LE_NEW_ARRAY(char, sizeof(InsertionRecord) + (count - ANY_NUMBER) * sizeof (LEGlyphID));

	insertion->position = position;
	insertion->count = count;

	growAmount += count - 1;

	if (append) {
		// insert on end of list...
		insertion->next = NULL;
		tail->next = insertion;
		tail = insertion;
	} else {
		// insert on front of list...
		insertion->next = head;
		head = insertion;
	}

	return insertion->glyphs;
}

le_bool InsertionList::applyInsertions(InsertionCallback *callback)
{
	for (InsertionRecord *rec = head; rec != NULL; rec = rec->next) {
		if (callback->applyInsertion(rec->position, rec->count, rec->glyphs)) {
			return TRUE;
		}
	}

	return FALSE;
}

GlyphIterator::GlyphIterator(LEGlyphID *&theGlyphs, GlyphPositionAdjustment *theGlyphPositionAdjustments, le_int32 *&theCharIndices, le_int32 theGlyphCount,
    le_bool rightToLeft, le_uint16 theLookupFlags, LETag theFeatureTag, const LETag **&theGlyphTags,
    const GlyphDefinitionTableHeader *theGlyphDefinitionTableHeader)
  : direction(1), position(-1), nextLimit(theGlyphCount), prevLimit(-1),
    cursiveFirstPosition(-1), cursiveLastPosition(-1), cursiveBaselineAdjustment(0),
    glyphsRef(&theGlyphs), glyphs(theGlyphs), glyphPositionAdjustments(theGlyphPositionAdjustments),
	charIndicesRef(&theCharIndices), charIndices(theCharIndices), glyphCount(theGlyphCount), insertionList(NULL), ownInsertionList(TRUE), srcIndex(-1), destIndex(-1),
	lookupFlags(theLookupFlags), featureTag(theFeatureTag), glyphTagsRef(&theGlyphTags), glyphTags(theGlyphTags),
    glyphClassDefinitionTable(NULL),
    markAttachClassDefinitionTable(NULL)

{
    if (theGlyphDefinitionTableHeader != NULL) {
        glyphClassDefinitionTable = theGlyphDefinitionTableHeader->getGlyphClassDefinitionTable();
        markAttachClassDefinitionTable = theGlyphDefinitionTableHeader->getMarkAttachClassDefinitionTable();
    }

    if (rightToLeft) {
        direction = -1;
        position = theGlyphCount;
        nextLimit = -1;
        prevLimit = theGlyphCount;
    }

	insertionList = new InsertionList(rightToLeft);
}

GlyphIterator::GlyphIterator(GlyphIterator &that)
  : InsertionCallback()
{
    direction    = that.direction;
    position     = that.position;
    nextLimit    = that.nextLimit;
    prevLimit    = that.prevLimit;

    cursiveFirstPosition = that.cursiveFirstPosition;
    cursiveLastPosition  = that.cursiveLastPosition;

    glyphsRef = that.glyphsRef;
    glyphs = that.glyphs;
    glyphPositionAdjustments = that.glyphPositionAdjustments;
    charIndicesRef = that.charIndicesRef;
    charIndices = that.charIndices;
    glyphCount = that.glyphCount;
    insertionList = that.insertionList;
    ownInsertionList = FALSE;
    srcIndex = that.srcIndex;
    destIndex = that.destIndex;
    lookupFlags = that.lookupFlags;
    featureTag = that.featureTag;
    glyphTagsRef = that.glyphTagsRef;
    glyphTags = that.glyphTags;
    glyphClassDefinitionTable = that.glyphClassDefinitionTable;
    markAttachClassDefinitionTable = that.markAttachClassDefinitionTable;
}

GlyphIterator::GlyphIterator(GlyphIterator &that, LETag newFeatureTag)
{
    direction    = that.direction;
    position     = that.position;
    nextLimit    = that.nextLimit;
    prevLimit    = that.prevLimit;

    cursiveFirstPosition = that.cursiveFirstPosition;
    cursiveLastPosition  = that.cursiveLastPosition;

	glyphsRef = that.glyphsRef;
	glyphs = that.glyphs;
    glyphPositionAdjustments = that.glyphPositionAdjustments;
	charIndicesRef = that.charIndicesRef;
	charIndices = that.charIndices;
	glyphCount = that.glyphCount;
	insertionList = that.insertionList;
	ownInsertionList = FALSE;
	srcIndex = that.srcIndex;
	destIndex = that.destIndex;
    lookupFlags = that.lookupFlags;
    featureTag = newFeatureTag;
	glyphTagsRef = that.glyphTagsRef;
    glyphTags = that.glyphTags;
    glyphClassDefinitionTable = that.glyphClassDefinitionTable;
    markAttachClassDefinitionTable = that.markAttachClassDefinitionTable;
}

GlyphIterator::GlyphIterator(GlyphIterator &that, le_uint16 newLookupFlags)
{
    direction    = that.direction;
    position     = that.position;
    nextLimit    = that.nextLimit;
    prevLimit    = that.prevLimit;


    cursiveFirstPosition = that.cursiveFirstPosition;
    cursiveLastPosition  = that.cursiveLastPosition;

	glyphsRef = that.glyphsRef;
    glyphs = that.glyphs;
    glyphPositionAdjustments = that.glyphPositionAdjustments;
	charIndicesRef = that.charIndicesRef;
	charIndices = that.charIndices;
	glyphCount = that.glyphCount;
	insertionList = that.insertionList;
	ownInsertionList = FALSE;
	srcIndex = that.srcIndex;
	destIndex = that.destIndex;
    lookupFlags = newLookupFlags;
    featureTag = that.featureTag;
	glyphTagsRef = that.glyphTagsRef;
    glyphTags = that.glyphTags;
    glyphClassDefinitionTable = that.glyphClassDefinitionTable;
    markAttachClassDefinitionTable = that.markAttachClassDefinitionTable;
}

GlyphIterator::GlyphIterator()
{
};

GlyphIterator::~GlyphIterator()
{
	if (ownInsertionList) {
		delete insertionList;
	}
}

void GlyphIterator::reset(le_uint16 newLookupFlags, LETag newFeatureTag)
{
	position    = prevLimit;
	featureTag  = newFeatureTag;
	lookupFlags = newLookupFlags;
}

LEGlyphID *GlyphIterator::insertGlyphs(le_int32 count)
{
	return insertionList->insert(position, count);
}

le_int32 GlyphIterator::applyInsertions()
{
	le_int32 growAmount = insertionList->getGrowAmount();

	if (growAmount == 0) {
		return glyphCount;
	}

	le_int32 newGlyphCount = glyphCount + growAmount;

	*glyphsRef      = glyphs      = (LEGlyphID *)    LE_GROW_ARRAY(glyphs,      newGlyphCount);
	*glyphTagsRef   = glyphTags   = (const LETag **) LE_GROW_ARRAY(glyphTags,   newGlyphCount);
	*charIndicesRef = charIndices = (le_int32 *)     LE_GROW_ARRAY(charIndices, newGlyphCount);

	srcIndex  = glyphCount - 1;
	destIndex = newGlyphCount - 1;

	// If the current position is at the end of the array
	// update it to point to the end of the new array. The
	// insertion callback will handle all other cases.
	if (position == glyphCount) {
		position = newGlyphCount;
	}

	insertionList->applyInsertions(this);

	insertionList->reset();

	if (direction < 0) {
		prevLimit = newGlyphCount;
	} else {
		nextLimit = newGlyphCount;
	}

	return glyphCount = newGlyphCount;
}

le_bool GlyphIterator::applyInsertion(le_int32 atPosition, le_int32 count, LEGlyphID newGlyphs[])
{
	// if the current position is within the block we're shifting
	// it needs to be updated to the current glyph's
	// new location.
	if (position >= atPosition && position <= srcIndex) {
		position += destIndex - srcIndex;
	}

	while (srcIndex > atPosition) {
		glyphs[destIndex]      = glyphs[srcIndex];
		glyphTags[destIndex]   = glyphTags[srcIndex];
		charIndices[destIndex] = charIndices[srcIndex];

		destIndex -= 1;
		srcIndex  -= 1;
	}

	for (le_int32 i = count - 1; i >= 0; i -= 1) {
		glyphs[destIndex]      = newGlyphs[i];
		glyphTags[destIndex]   = glyphTags[atPosition];
		charIndices[destIndex] = charIndices[atPosition];

		destIndex -= 1;
	}

	// the source glyph we're pointing at
	// just got replaced by the insertion
	srcIndex -= 1;

	return FALSE;
}

le_int32 GlyphIterator::getCurrStreamPosition() const
{
    return position;
}

le_bool GlyphIterator::isRightToLeft() const
{
    return direction < 0;
}

le_bool GlyphIterator::ignoresMarks() const
{
    return (lookupFlags & lfIgnoreMarks) != 0;
}

le_bool GlyphIterator::baselineIsLogicalEnd() const
{
    return (lookupFlags & lfBaselineIsLogicalEnd) != 0;
}

le_bool GlyphIterator::hasCursiveFirstExitPoint() const
{
    return cursiveFirstPosition >= 0;
}

le_bool GlyphIterator::hasCursiveLastExitPoint() const
{
    return cursiveLastPosition >= 0;
}

LEGlyphID GlyphIterator::getCurrGlyphID() const
{
    if (direction < 0) {
        if (position <= nextLimit || position >= prevLimit) {
            return 0xFFFF;
        }
    } else {
        if (position <= prevLimit || position >= nextLimit) {
            return 0xFFFF;
        }
    }

    return glyphs[position];
}

LEGlyphID GlyphIterator::getCursiveLastGlyphID() const
{
    if (direction < 0) {
        if (cursiveLastPosition <= nextLimit || cursiveLastPosition >= prevLimit) {
            return 0xFFFF;
        }
    } else {
        if (cursiveLastPosition <= prevLimit || cursiveLastPosition >= nextLimit) {
            return 0xFFFF;
        }
    }

    return glyphs[cursiveLastPosition];
}

void GlyphIterator::getCursiveLastExitPoint(LEPoint &exitPoint) const
{
    if (cursiveLastPosition >= 0) {
        exitPoint = cursiveLastExitPoint;
    }
}

float GlyphIterator::getCursiveBaselineAdjustment() const
{
    return cursiveBaselineAdjustment;
}

void GlyphIterator::getCurrGlyphPositionAdjustment(GlyphPositionAdjustment &adjustment) const
{
    if (direction < 0)
    {
        if (position <= nextLimit || position >= prevLimit)
        {
            return;
        }
    } else {
        if (position <= prevLimit || position >= nextLimit) {
            return;
        }
    }

    adjustment = glyphPositionAdjustments[position];
}

void GlyphIterator::getCursiveLastPositionAdjustment(GlyphPositionAdjustment &adjustment) const
{
    if (direction < 0)
    {
        if (cursiveLastPosition <= nextLimit || cursiveLastPosition >= prevLimit)
        {
            return;
        }
    } else {
        if (cursiveLastPosition <= prevLimit || cursiveLastPosition >= nextLimit) {
            return;
        }
    }

    adjustment = glyphPositionAdjustments[cursiveLastPosition];
}

void GlyphIterator::setCurrGlyphID(TTGlyphID glyphID)
{
    glyphs[position] = LE_SET_GLYPH(glyphs[position], glyphID);
}

void GlyphIterator::setCurrStreamPosition(le_int32 newPosition)
{
    cursiveFirstPosition      = -1;
    cursiveLastPosition       = -1;
    cursiveBaselineAdjustment =  0;

    if (direction < 0) {
        if (newPosition >= prevLimit) {
            position = prevLimit;
            return;
        }

        if (newPosition <= nextLimit) {
            position = nextLimit;
            return;
        }
    } else {
        if (newPosition <= prevLimit) {
            position = prevLimit;
            return;
        }

        if (newPosition >= nextLimit) {
            position = nextLimit;
            return;
        }
    }

    position = newPosition - direction;
    next();
}

void GlyphIterator::setCurrGlyphPositionAdjustment(const GlyphPositionAdjustment *adjustment)
{
    if (direction < 0) {
        if (position <= nextLimit || position >= prevLimit) {
            return;
        }
    } else {
        if (position <= prevLimit || position >= nextLimit) {
            return;
        }
    }

    glyphPositionAdjustments[position] = *adjustment;
}

void GlyphIterator::setCurrGlyphBaseOffset(le_int32 baseOffset)
{
    if (direction < 0) {
        if (position <= nextLimit || position >= prevLimit) {
            return;
        }
    } else {
        if (position <= prevLimit || position >= nextLimit) {
            return;
        }
    }

    glyphPositionAdjustments[position].setBaseOffset(baseOffset);
}

void GlyphIterator::adjustCurrGlyphPositionAdjustment(float xPlacementAdjust, float yPlacementAdjust,
                                                      float xAdvanceAdjust, float yAdvanceAdjust)
{
    if (direction < 0) {
        if (position <= nextLimit || position >= prevLimit) {
            return;
        }
    } else {
        if (position <= prevLimit || position >= nextLimit) {
            return;
        }
    }

    glyphPositionAdjustments[position].adjustXPlacement(xPlacementAdjust);
    glyphPositionAdjustments[position].adjustYPlacement(yPlacementAdjust);
    glyphPositionAdjustments[position].adjustXAdvance(xAdvanceAdjust);
    glyphPositionAdjustments[position].adjustYAdvance(yAdvanceAdjust);
}

void GlyphIterator::setCursiveFirstExitPoint()
{
    if (direction < 0) {
        if (position <= nextLimit || position >= prevLimit) {
            return;
        }
    } else {
        if (position <= prevLimit || position >= nextLimit) {
            return;
        }
    }

    cursiveFirstPosition = position;
}

void GlyphIterator::resetCursiveLastExitPoint()
{
    if ((lookupFlags & lfBaselineIsLogicalEnd) != 0 && cursiveFirstPosition >= 0 && cursiveLastPosition >= 0) {
        le_int32 savePosition = position, saveLimit = nextLimit;

        position  = cursiveFirstPosition - direction;
        nextLimit = cursiveLastPosition  + direction;

        while (nextInternal()) {
            glyphPositionAdjustments[position].adjustYPlacement(-cursiveBaselineAdjustment);
        }

        position  = savePosition;
        nextLimit = saveLimit;
    }

    cursiveLastPosition       = -1;
    cursiveFirstPosition      = -1;
    cursiveBaselineAdjustment =  0;
}

void GlyphIterator::setCursiveLastExitPoint(LEPoint &exitPoint)
{
    if (direction < 0) {
        if (position <= nextLimit || position >= prevLimit) {
            return;
        }
    } else {
        if (position <= prevLimit || position >= nextLimit) {
            return;
        }
    }

    cursiveLastPosition  = position;
    cursiveLastExitPoint = exitPoint;

}

void GlyphIterator::setCursiveBaselineAdjustment(float adjustment)
{
    cursiveBaselineAdjustment = adjustment;
}

void GlyphIterator::adjustCursiveLastGlyphPositionAdjustment(float xPlacementAdjust, float yPlacementAdjust,
                                              float xAdvanceAdjust, float yAdvanceAdjust)
{
    if (direction < 0) {
        if (cursiveLastPosition <= nextLimit || cursiveLastPosition >= prevLimit) {
            return;
        }
    } else {
        if (cursiveLastPosition <= prevLimit || cursiveLastPosition >= nextLimit) {
            return;
        }
    }

    glyphPositionAdjustments[cursiveLastPosition].adjustXPlacement(xPlacementAdjust);
    glyphPositionAdjustments[cursiveLastPosition].adjustYPlacement(yPlacementAdjust);
    glyphPositionAdjustments[cursiveLastPosition].adjustXAdvance(xAdvanceAdjust);
    glyphPositionAdjustments[cursiveLastPosition].adjustYAdvance(yAdvanceAdjust);
}

le_bool GlyphIterator::filterGlyph(le_uint32 index) const
{
    LEGlyphID glyphID = glyphs[index];
    le_int32 glyphClass = gcdNoGlyphClass;

    if (LE_GET_GLYPH(glyphID) >= 0xFFFE) {
        return TRUE;
    }

    if (glyphClassDefinitionTable != NULL) {
        glyphClass = glyphClassDefinitionTable->getGlyphClass(glyphID);
    }

    switch (glyphClass)
    {
    case gcdNoGlyphClass:
        return FALSE;

    case gcdSimpleGlyph:
        return (lookupFlags & lfIgnoreBaseGlyphs) != 0;

    case gcdLigatureGlyph:
        return (lookupFlags & lfIgnoreLigatures) != 0;

    case gcdMarkGlyph:
    {
        if ((lookupFlags & lfIgnoreMarks) != 0) {
            return TRUE;
        }

        le_uint16 markAttachType = (lookupFlags & lfMarkAttachTypeMask) >> lfMarkAttachTypeShift;

        if ((markAttachType != 0) && (markAttachClassDefinitionTable != NULL)) {
            return markAttachClassDefinitionTable->getGlyphClass(glyphID) != markAttachType;
        }

        return FALSE;
    }

    case gcdComponentGlyph:
        return (lookupFlags & lfIgnoreBaseGlyphs) != 0;

    default:
        return FALSE;
    }
}

const LETag emptyTag = 0;
const LETag defaultTag = 0xFFFFFFFF;

le_bool GlyphIterator::hasFeatureTag() const
{
    if (featureTag == defaultTag || featureTag == emptyTag) {
        return TRUE;
    }

    if (glyphTags != NULL) {
        const LETag *tagList = glyphTags[position];

        for (le_int32 tag = 0; tagList[tag] != emptyTag; tag += 1) {
            if (tagList[tag] == featureTag) {
                return TRUE;
            }
        }
    }

    return FALSE;
}

le_bool GlyphIterator::findFeatureTag()
{
    while (nextInternal()) {
        if (hasFeatureTag()) {
            prevInternal();
            return TRUE;
        }
    }

    return FALSE;
}


le_bool GlyphIterator::nextInternal(le_uint32 delta)
{
    le_int32 newPosition = position;

    while (newPosition != nextLimit && delta > 0) {
        do {
            newPosition += direction;
        } while (newPosition != nextLimit && filterGlyph(newPosition));

        delta -= 1;
    }

    position = newPosition;

    return position != nextLimit;
}

le_bool GlyphIterator::next(le_uint32 delta)
{
    return nextInternal(delta) && hasFeatureTag();
}

le_bool GlyphIterator::prevInternal(le_uint32 delta)
{
    le_int32 newPosition = position;

    while (newPosition != prevLimit && delta > 0) {
        do {
            newPosition -= direction;
        } while (newPosition != prevLimit && filterGlyph(newPosition));

        delta -= 1;
    }

    position = newPosition;

    return position != prevLimit;
}

le_bool GlyphIterator::prev(le_uint32 delta)
{
    return prevInternal(delta) && hasFeatureTag();
}

le_int32 GlyphIterator::getMarkComponent(le_int32 markPosition) const
{
    le_int32 component = 0;
    le_int32 posn;

    for (posn = position; posn != markPosition; posn += direction) {
        if (glyphs[posn] == 0xFFFE) {
            component += 1;
        }
    }

    return component;
}

// This is basically prevInternal except that it
// doesn't take a delta argument, and it doesn't
// filter out 0xFFFE glyphs.
le_bool GlyphIterator::findMark2Glyph()
{
    le_int32 newPosition = position;

    do {
        newPosition -= direction;
    } while (newPosition != prevLimit && glyphs[newPosition] != 0xFFFE && filterGlyph(newPosition));

    position = newPosition;

    return position != prevLimit;
}

U_NAMESPACE_END


syntax highlighted by Code2HTML, v. 0.9.1