/*
 *
 * (C) Copyright IBM Corp. 1998-2008 - All Rights Reserved
 *
 */

#include "LETypes.h"
#include "GlyphPositionAdjustments.h"
#include "LEGlyphStorage.h"
#include "LEFontInstance.h"

U_NAMESPACE_BEGIN

#define CHECK_ALLOCATE_ARRAY(array, type, size) \
    if (array == NULL) { \
        array = (type *) new type[size]; \
    }

GlyphPositionAdjustments::GlyphPositionAdjustments(le_int32 glyphCount)
    : fGlyphCount(glyphCount), fEntryExitPoints(NULL), fAdjustments(NULL)
{
    fAdjustments = (Adjustment *) new Adjustment[glyphCount];
}

GlyphPositionAdjustments::~GlyphPositionAdjustments()
{
    delete[] fEntryExitPoints;
    delete[] fAdjustments;
}

const LEPoint *GlyphPositionAdjustments::getEntryPoint(le_int32 index, LEPoint &entryPoint) const
{
    if (fEntryExitPoints == NULL) {
        return NULL;
    }

    return fEntryExitPoints[index].getEntryPoint(entryPoint);
}

const LEPoint *GlyphPositionAdjustments::getExitPoint(le_int32 index, LEPoint &exitPoint)const
{
    if (fEntryExitPoints == NULL) {
        return NULL;
    }

    return fEntryExitPoints[index].getExitPoint(exitPoint);
}

void GlyphPositionAdjustments::clearEntryPoint(le_int32 index)
{
    CHECK_ALLOCATE_ARRAY(fEntryExitPoints, EntryExitPoint, fGlyphCount);

    fEntryExitPoints[index].clearEntryPoint();
}

void GlyphPositionAdjustments::clearExitPoint(le_int32 index)
{
    CHECK_ALLOCATE_ARRAY(fEntryExitPoints, EntryExitPoint, fGlyphCount);

    fEntryExitPoints[index].clearExitPoint();
}

void GlyphPositionAdjustments::setEntryPoint(le_int32 index, LEPoint &newEntryPoint, le_bool baselineIsLogicalEnd)
{
    CHECK_ALLOCATE_ARRAY(fEntryExitPoints, EntryExitPoint, fGlyphCount);

    fEntryExitPoints[index].setEntryPoint(newEntryPoint, baselineIsLogicalEnd);
}

void GlyphPositionAdjustments::setExitPoint(le_int32 index, LEPoint &newExitPoint, le_bool baselineIsLogicalEnd)
{
    CHECK_ALLOCATE_ARRAY(fEntryExitPoints, EntryExitPoint, fGlyphCount);

    fEntryExitPoints[index].setExitPoint(newExitPoint, baselineIsLogicalEnd);
}

void GlyphPositionAdjustments::setCursiveGlyph(le_int32 index, le_bool baselineIsLogicalEnd)
{
    CHECK_ALLOCATE_ARRAY(fEntryExitPoints, EntryExitPoint, fGlyphCount);

    fEntryExitPoints[index].setCursiveGlyph(baselineIsLogicalEnd);
}

void GlyphPositionAdjustments::applyCursiveAdjustments(LEGlyphStorage &glyphStorage, le_bool rightToLeft, const LEFontInstance *fontInstance)
{
    if (! hasCursiveGlyphs()) {
        return;
    }

    le_int32 start = 0, end = fGlyphCount, dir = 1;
    le_int32 firstExitPoint = -1, lastExitPoint = -1;
    LEPoint entryAnchor, exitAnchor, pixels;
    LEGlyphID lastExitGlyphID = 0;
    float baselineAdjustment = 0;

    // This removes a possible warning about
    // using exitAnchor before it's been initialized.
    exitAnchor.fX = exitAnchor.fY = 0;

    if (rightToLeft) {
        start = fGlyphCount - 1;
        end = -1;
        dir = -1;
    }

    for (le_int32 i = start; i != end; i += dir) {
        LEGlyphID glyphID = glyphStorage[i];

        if (isCursiveGlyph(i)) {
            if (lastExitPoint >= 0 && getEntryPoint(i, entryAnchor) != NULL) {
                float anchorDiffX = exitAnchor.fX - entryAnchor.fX;
                float anchorDiffY = exitAnchor.fY - entryAnchor.fY;

                baselineAdjustment += anchorDiffY;
                adjustYPlacement(i, baselineAdjustment);

                if (rightToLeft) {
                    LEPoint secondAdvance;

                    fontInstance->getGlyphAdvance(glyphID, pixels);
                    fontInstance->pixelsToUnits(pixels, secondAdvance);

                    adjustXAdvance(i, -(anchorDiffX + secondAdvance.fX));
                } else {
                    LEPoint firstAdvance;

                    fontInstance->getGlyphAdvance(lastExitGlyphID, pixels);
                    fontInstance->pixelsToUnits(pixels, firstAdvance);

                    adjustXAdvance(lastExitPoint, anchorDiffX - firstAdvance.fX);
                }
            }

            lastExitPoint = i;

            if (getExitPoint(i, exitAnchor) != NULL) {
                if (firstExitPoint < 0) {
                    firstExitPoint = i;
                }

                lastExitGlyphID = glyphID;
            } else {
                if (baselineIsLogicalEnd(i) && firstExitPoint >= 0 && lastExitPoint >= 0) {
                    le_int32 limit = lastExitPoint /*+ dir*/;
                    LEPoint dummyAnchor;

                    if (getEntryPoint(i, dummyAnchor) != NULL) {
                        limit += dir;
                    }

                    for (le_int32 j = firstExitPoint; j != limit; j += dir) {
                        if (isCursiveGlyph(j)) {
                            adjustYPlacement(j, -baselineAdjustment);
                        }
                    }
                }

                firstExitPoint = lastExitPoint = -1;
                baselineAdjustment = 0;
            }
        }
    }
}

LEPoint *GlyphPositionAdjustments::EntryExitPoint::getEntryPoint(LEPoint &entryPoint) const
{
    if (fFlags & EEF_HAS_ENTRY_POINT) {
        entryPoint = fEntryPoint;
        return &entryPoint;
    }

    return NULL;
}

LEPoint *GlyphPositionAdjustments::EntryExitPoint::getExitPoint(LEPoint &exitPoint) const
{
    if (fFlags & EEF_HAS_EXIT_POINT) {
        exitPoint = fExitPoint;
        return &exitPoint;
    }

    return NULL;
}

U_NAMESPACE_END