/// Copyright (c) 2009 Ruben Penalva. All rights reserved.
/// Redistribution and use in source and binary forms, with or without modification, 
/// are permitted provided that the following conditions are met:
///    1. Redistributions of source code must retain the above copyright notice, this 
///    list of conditions and the following disclaimer.
///    2. Redistributions in binary form must reproduce the above copyright notice,
///    this list of conditions and the following disclaimer in the documentation and/or
///    other materials provided with the distribution.
///    3. The name of the author may not be used to endorse or promote products derived
///    from this software without specific prior written permission.
///
/// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
/// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
/// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 
/// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
/// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
/// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
/// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 
/// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

/// @file GlyphOutline.cpp
/// This source file is part of LetterCDT test.
/// The file holds the GlyphOutline class interface.
/// @author Ruben Penalva (ruben.penalva@rpenalva.com)
#include "GlyphOutline.h"

using namespace LetterCDT;

#include "freetype/ftoutln.h"
#include <assert.h>

GlyphOutline::GlyphOutline(wchar_t charCode, FT_Face face)
{
	LoadGlyph(charCode, face);
}

GlyphOutline::~GlyphOutline(void)
{
}

const std::vector<Point>& GlyphOutline::GetPoints(void) const
{
	return m_points;
}

const std::vector<Line>& GlyphOutline::GetSegments(void) const
{
	return m_segments;
}

const std::vector<int>& GlyphOutline::GetContours(void) const
{
	return m_contours;
}

const std::vector<ContourType>& GlyphOutline::GetContoursType(void) const
{
	return m_contoursTypes;
}

int GlyphOutline::GetWidth(void) const
{
	return m_width;
}

int GlyphOutline::GetAdvance(void) const
{
	return m_advance;
}

void GlyphOutline::LoadGlyph(wchar_t charCode, FT_Face face)
{
	FT_UInt glyphIndex = FT_Get_Char_Index(face, charCode);
	FT_Error error = FT_Load_Glyph(face, glyphIndex, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP);
    if(error)
    {
		throw std::wstring(L"[ERROR] Unable to load glyph. Method: LoadFontFace.");
    }

	FT_GlyphSlot glyphSlot = face->glyph;
	if(FT_GLYPH_FORMAT_OUTLINE != glyphSlot->format)
    {
		throw std::wstring(L"[ERROR] Invalid_Outline. Method: LoadFontFace.");
    }

	m_advance = glyphSlot->metrics.horiAdvance;
	m_width = glyphSlot->metrics.width;

	FT_Outline outline = glyphSlot->outline;
	int contoursCount = outline.n_contours;
	int outlineFlags = outline.flags;

	/// Gets the orientation convention.
	FT_Orientation orientation = FT_Outline_Get_Orientation(&outline);

	m_points.reserve(outline.n_points);

	/// Load points
	for (int i = 0; i < outline.n_points; ++i)
	{
		FT_Vector& point = outline.points[i];
		m_points.push_back(Point(point.x, point.y));
	}

	/// Load segments
	for (int i = 0, startIndex = 0, pointsCount = 0; i < contoursCount; ++i)
	{		
		int endIndex = outline.contours[i];
		pointsCount += endIndex - startIndex + 1;

		assert(pointsCount - 1 > 0);

		m_contours.push_back(m_segments.size());

		/// Create all the segments from indexed points
		for (int j = startIndex; j < pointsCount - 1; ++j)
		{			
			FT_Vector& start = outline.points[j];
			FT_Vector& end = outline.points[j + 1];

			m_segments.push_back(Line(Point(start.x, start.y), Point(end.x, end.y)));	
		}

		assert(pointsCount - 1 > 0);

		/// Close the contour, adding one last segment
		/// from the last point to the first point
		FT_Vector& start = outline.points[pointsCount - 1];
		FT_Vector& end = outline.points[startIndex];

		m_segments.push_back(Line(Point(start.x, start.y), Point(end.x, end.y)));

		m_contours.push_back(m_segments.size() - 1);

		int lastContourIndex = (m_contours.size() / 2) - 1;

		/// Get contour type from orientation convention
		/// and its orientation (bool counterClockWise)
		const Line& line = m_segments[0];
		bool counterClockWise = ContourCounterClockWise(m_contours[lastContourIndex * 2],
														m_contours[lastContourIndex * 2 + 1]);
		switch (orientation)
		{
			case FT_ORIENTATION_TRUETYPE:
				if (counterClockWise)
				{
					m_contoursTypes.push_back(Inner);
				}
				else
				{
					m_contoursTypes.push_back(Outer);
				}
				break;

			case FT_ORIENTATION_POSTSCRIPT:
				if (counterClockWise)
				{
					m_contoursTypes.push_back(Outer);
				}
				else
				{
					m_contoursTypes.push_back(Inner);
				}
				break;
			
			case FT_ORIENTATION_NONE:
				throw std::wstring(L"[ERROR] The orientation cannot be determined. That is, different parts of the glyph have different orientation. Method: LoadFontFace.");
				break;
		}

		startIndex = endIndex + 1;
	}
}

bool GlyphOutline::ContourCounterClockWise(int segmentStartIndex, int segmentEndIndex)
{		
	/// Calculating the polygon area. 
	/// A > 0 => polygon counter clockwise oriented
	int area = 0;

	int segmentsCount = segmentEndIndex + 1;
	for (int i = segmentStartIndex; i < segmentsCount; ++i)
	{
		const Line& segment = m_segments[i];
		const Point& start = segment.m_start;
		const Point& end = segment.m_end;

		area += ((start.m_x * end.m_y) - (end.m_x * start.m_y));
	}

	area *= 0.5f;
		
	return area > 0;
}