#include "FreeTypeGlyphPathIterator.h"
#include <assert.h>

/**
 * creates a FreeTypeGlyphPathIterator from specified FreeTypeGlyphVector
 */
FreeTypeGlyphPathIterator::FreeTypeGlyphPathIterator(FreeTypeGlyphVector* gv, int glyphIndex) : glyphVector(gv),
									    currentOutlineIndex(0),
									    currentContourIndex(0),
									    currentPointIndex(0),
									    pendingSegClose(false)
{
    int startIndex;
    if (glyphIndex == -1)
    {
	this->numOutlines = gv->getNumGlyphs();
	startIndex = 0;
    }
    else
    {
	this->numOutlines = 1;
	startIndex = glyphIndex;
    }
    this->outlines = (FT_Outline*) ::malloc(sizeof(FT_Outline) * this->numOutlines);

    // FT_Outlineをコピーする
    for (int i = 0; i < numOutlines; ++i)
    {
	FT_Outline src = gv->getOutline(i);
	int error = FT_Outline_New(gv->getLibrary(), src.n_points, src.n_contours, &this->outlines[startIndex + i]);
	if (error)
	{
	    break;
	}
	error = FT_Outline_Copy(&src, &this->outlines[i]);
	if (error)
	{
	    return;
	}
	if (glyphIndex != -1)
	{
	    // FT_Outlineの位置を変更する
	    const FT_Vector& glyphPos = gv->getGlyphPosition(startIndex + i);
	    FT_Outline_Translate(&this->outlines[i], glyphPos.x, glyphPos.y);
	}
    }
}

/**
 * despose this FreeTypeGlyphPathIterator
 */
FreeTypeGlyphPathIterator::~FreeTypeGlyphPathIterator()
{
    for (int i = 0; i < numOutlines; ++i)
    {
	int error = FT_Outline_Done(glyphVector->getLibrary(), &outlines[i]);
	if (error) 
	{
	    break;
	}
    }
    free(outlines);
}

bool FreeTypeGlyphPathIterator::isDone() const
{
    return currentOutlineIndex >= numOutlines;
}

void FreeTypeGlyphPathIterator::next()
{
    if (! isDone() && ! pendingSegClose)
    {
	FT_Outline& currentOutline = outlines[currentOutlineIndex];
	FT_Vector* points = currentOutline.points;

	currentPointIndex++;
	if (currentPointIndex >= currentOutline.n_points)
	{
	    // 次のFT_Outlineへ移動	
	    currentOutlineIndex++;
	    currentPointIndex = 0;
	    currentContourIndex = 0;
	}
	else if (currentPointIndex > currentOutline.contours[currentContourIndex])
	{
	    // 次の輪郭へ移動
	    currentContourIndex++;
	    if (currentContourIndex >= currentOutline.n_contours)
	    {
		// 次のFT_Outlineへ移動	
		currentOutlineIndex++;
		currentPointIndex = 0;
		currentContourIndex = 0;
	    }
	}
    }
}

// PathIterator.currentSegment()の返す値と同じ
#define SEG_MOVETO 0
#define SEG_LINETO 1
#define SEG_QUADTO 2
#define SEG_CUBICTO 3
#define SEG_CLOSE 4
int FreeTypeGlyphPathIterator::currentSegment(FT_Pos* coords)
{
    if (isDone())
    {
	return SEG_CLOSE;
    }

    int ret = -1;
    FT_Outline& currentOutline = outlines[currentOutlineIndex];
    short* contours = currentOutline.contours;
    FT_Vector* points = currentOutline.points;
    char* tags = currentOutline.tags;
    bool close_point = false;

    if (currentPointIndex >= currentOutline.n_points - 1
	|| (currentPointIndex == contours[currentContourIndex]))
    {
	// SEG_CLOSE
	if (this->pendingSegClose)
	{
	    ret = SEG_CLOSE;
	    this->pendingSegClose = false;
	}
	else
	{
	    this->pendingSegClose = true;
	}
    }
    
    if (ret != SEG_CLOSE)
    {
	if (currentPointIndex == 0 || (currentContourIndex > 0 && currentPointIndex == contours[currentContourIndex - 1] + 1))
	{
	    // 最初の点
	    ret = SEG_MOVETO;
	    *coords++ = points[currentPointIndex].x;
	    *coords++ = points[currentPointIndex].y;
	}
	else if (tags[currentPointIndex] & FT_Curve_Tag_On)
	{
	    // オンカーブ点
	    ret = SEG_LINETO;
	    *coords++ = points[currentPointIndex].x;
	    *coords++ = points[currentPointIndex].y;
	}
	else if (tags[currentPointIndex] & FT_Curve_Tag_Cubic)
	{
	    // ベジエ曲線はサポートしていない
	    assert(false);

	} else {
	    // 2次 B-スプライン曲線
	    ret = SEG_QUADTO;
	    // 次の点の場所を求める
	    int nextPointIndex;
	    if (this->pendingSegClose)
	    {
		// もう次に点がない
		// 次の点は、輪郭の開始位置
		if (currentContourIndex == 0) 
		{
		    nextPointIndex = 0;
		}
		else
		{
		    // 
		    nextPointIndex = contours[currentContourIndex - 1] + 1;
		}
	    }
	    else
	    {
		// 次にも点がある
		nextPointIndex = currentPointIndex + 1;
	    }
	    if (tags[nextPointIndex] & FT_Curve_Tag_On || this->pendingSegClose)
	    {
		// 次の点がオンカーブである場合
		*coords++ = points[currentPointIndex].x;
		*coords++ = points[currentPointIndex].y;
		*coords++ = points[nextPointIndex].x;
		*coords++ = points[nextPointIndex].y;
	    }
	    else
	    {
		// 次の点がオフカーブである場合
		*coords++ = points[currentPointIndex].x;
		*coords++ = points[currentPointIndex].y;
		*coords++ = (points[currentPointIndex].x + points[nextPointIndex].x) / 2;
		*coords++ = (points[currentPointIndex].y + points[nextPointIndex].y) / 2;
	    }
	}
    }
    assert(ret >= 0);
    return ret;
}