/// 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 LetterCDTTestLogic.cpp
/// This source file is part of 2dRender LetterCDT Test.
/// The file holds the LetterCDTTestLogic class implementation.
/// @author Ruben Penalva (ruben.penalva@rpenalva.com)

#include "LetterCDTTestLogic.h"

#include "GlyphOutline.h"
#include "Triangulator.h"

#include <assert.h>

using namespace LetterCDT;

LetterCDTTestLogic::LetterCDTTestLogic(void)	:	m_glyphOutlineLoader(L"data\\arial.ttf"), 
													m_enabledPoints(true), m_enabledSegments(true),
													m_enabledTriangles(true)
{	
}

LetterCDTTestLogic::~LetterCDTTestLogic(void)
{
}
	
void LetterCDTTestLogic::PushLetter(wchar_t charCode)
{
	GlyphOutline* glyphOutline = m_glyphOutlineLoader.GetGlyphOutline(charCode);

	/// Compute the translation of the next letter
	int translationsCount = m_translations.size();
	int glyphAdvance = glyphOutline->GetAdvance() / 64.0f;
	float translation = - 22.0f;
	if (translationsCount == 0)
	{
		m_translations.push_back(glyphAdvance);
	}
	else
	{
		float stringWidth = m_translations[translationsCount - 1];
		if (stringWidth + glyphAdvance > 44.0f)
		{
			return;
		}

		translation += stringWidth;
		m_translations.push_back(glyphAdvance + stringWidth);
	}

	Point maxPoint(-10000.0f, -10000.0f);

	/// Add new points
	const std::vector<Point>& points = glyphOutline->GetPoints();
	int pointsCount = points.size();
	
	/// Check if theres a space.
	if (pointsCount == 0)
	{
		return;
	}

	int pointsInBufferCount = m_points.size();
	for (int i = 0; i < pointsCount; ++i)
	{
		const Point& point = points[i];
		Point newPoint((point.m_x / 64.0f) + translation, point.m_y / 64.0f);

		m_points.push_back(newPoint);

		if (point.m_x > maxPoint.m_x)
		{
			maxPoint = point;
		}
	}	
	
	/// Add new segments
	const std::vector<Line>& segments = glyphOutline->GetSegments();
	int segmentsCount = segments.size();
	for (int i = 0; i < segmentsCount; ++i)
	{
		const Point& start = segments[i].m_start;
		const Point& end = segments[i].m_end;
		m_lines.push_back(Line(Point((start.m_x  / 64.0f) + translation, start.m_y / 64.0f), 
							   Point((end.m_x / 64.0f) + translation, end.m_y / 64.0f)));
	}

	/// Compute holes
	std::vector<Point> holes;
	const std::vector<int>& contours = glyphOutline->GetContours();
	const std::vector<ContourType>& contoursTypes = glyphOutline->GetContoursType();
	int contoursCount = contours.size() / 2;
	
	assert(contoursTypes.size() == contoursCount);

	for (int i = 0; i < contoursCount; ++i)
	{
		if (contoursTypes[i] == Outer)
		{
			continue;
		}

		int startIndex = contours[i * 2];
		int endIndex = contours[i * 2 + 1];
			
		Point middlePoint;
		for (int j = startIndex; j < endIndex + 1; ++j)
		{
			const Line& segment = segments[j];
			middlePoint += segment.m_start;
		}
		
		int contourPointsCount = endIndex - startIndex + 1;
		middlePoint /= contourPointsCount;

		holes.push_back(middlePoint);
	}

	/// Triangulate
	Triangulator triangulator(segments, contours, holes);
	const std::vector<int>& indices = triangulator.GetIndices();
	int indicesCount = indices.size();
	for (int i = 0; i < indicesCount; ++i)
	{
		m_triangles.push_back(indices[i] + pointsInBufferCount);
	}
}

void LetterCDTTestLogic::Reset(void)
{	
	m_points.clear();
	m_lines.clear();
	m_triangles.clear();
	m_translations.clear();
}

void LetterCDTTestLogic::EnablePoints(void)
{
	m_enabledPoints = !m_enabledPoints;
}

void LetterCDTTestLogic::EnableSegments(void)
{
	m_enabledSegments = !m_enabledSegments;
}

void LetterCDTTestLogic::EnableTriangles(void)
{
	m_enabledTriangles = !m_enabledTriangles;
}

bool LetterCDTTestLogic::ArePointsEnabled(void)
{
	return m_enabledPoints;
}

bool LetterCDTTestLogic::AreSegmentsEnabled(void)
{
	return m_enabledSegments;
}

bool LetterCDTTestLogic::AreTrianglesEnabled(void)
{
	return m_enabledTriangles;
}

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

const std::vector<Line>& LetterCDTTestLogic::GetLines(void) const
{
	return m_lines;
}	

const std::vector<int>& LetterCDTTestLogic::GetTriangles(void) const
{
	return m_triangles;
}