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

#include "Triangulator.h"

#include "triangle.h"

using namespace LetterCDT;

Triangulator::Triangulator(const std::vector<Line>& segments,
						   const std::vector<int>& contours,
						   const std::vector<Point>& holes)
{
	Triangulate(segments, contours, holes);
}

Triangulator::~Triangulator(void)
{
}

const std::vector<int>& Triangulator::GetIndices(void) const
{
	return m_indices;
}

void Triangulator::Triangulate(const std::vector<Line>& segments,
							   const std::vector<int>& contours,
							   const std::vector<Point>& holes)
{
	int contoursCount = contours.size() / 2;
	int segmentsCount = segments.size();
	int pointsCount = segmentsCount;
	int triangleSegmentsCount = segmentsCount;
	
	REAL* trianglePoints = new REAL[pointsCount * 2];
	int* trianglePointsMarkers = new int[pointsCount];
	int* triangleSegments = new int[triangleSegmentsCount * 2];
	int* triangleSegmentsMarkers = new int[triangleSegmentsCount];

	for (int i = 0, pointIndex = 0, segmentId = 2, segmentIndex = 0; i < contoursCount; ++i, ++segmentId)
	{
		int startIndex = contours[i * 2];
		int endIndex = contours[i * 2 + 1];

		int startPointIndex = pointIndex;

		/// Loop through all the segments of the contour
		/// except the last one. This segment its inserted
		/// manually joining it to the start
		for (int j = startIndex; j < endIndex; ++j)
		{
			const Line& segment = segments[j];

			trianglePoints[pointIndex * 2] = segment.m_start.m_x;
			trianglePoints[pointIndex * 2 + 1] = segment.m_start.m_y;
			
			trianglePointsMarkers[pointIndex] = segmentId;

			triangleSegments[segmentIndex * 2] = pointIndex;
			triangleSegments[segmentIndex * 2 + 1] = pointIndex + 1;

			triangleSegmentsMarkers[segmentIndex] = segmentId;

			++pointIndex;
			++segmentIndex;
		}

		const Line& segment = segments[endIndex];

		trianglePoints[pointIndex * 2] = segment.m_start.m_x;
		trianglePoints[pointIndex * 2 + 1] = segment.m_start.m_y;
			
		trianglePointsMarkers[pointIndex] = segmentId;

		triangleSegments[segmentIndex * 2] = pointIndex;
		triangleSegments[segmentIndex * 2 + 1] = startIndex;

		triangleSegmentsMarkers[segmentIndex] = segmentId;

		++pointIndex;
		++segmentIndex;
	}

	/// Create holes
	int triangleHolesCount = holes.size();
	REAL* triangleHoles = new REAL[triangleHolesCount * 2];
	for (int i = 0; i < triangleHolesCount; i += 2)
	{
		const Point& holePoint = holes[i];
		triangleHoles[i] = holePoint.m_x;
		triangleHoles[i + 1] = holePoint.m_y;
	}
	
	/// Triangle input and output structures
	struct triangulateio in, out;
	memset(&in, 0, sizeof(triangulateio));
	memset(&out, 0, sizeof(triangulateio));

	/// Define input points
	in.numberofpoints = pointsCount;
	in.pointlist = trianglePoints;
	in.pointmarkerlist = trianglePointsMarkers;

	/// Define segments
	in.numberofsegments = triangleSegmentsCount;
	in.segmentlist = triangleSegments;
	in.segmentmarkerlist = triangleSegmentsMarkers;

	/// Define holes
	in.numberofholes = triangleHolesCount;
	in.holelist = triangleHoles;

#ifdef _DEBUG
	triangulate("pz", &in, &out, 0);
#else
	triangulate("pzQ", &in, &out, 0);
#endif

	delete[] trianglePoints;
	delete[] trianglePointsMarkers;
	delete[] triangleSegments;
	delete[] triangleSegmentsMarkers;
	delete[] triangleHoles;

	int trianglesCount = out.numberoftriangles * 3;
	for (int i = 0; i < trianglesCount; ++i)
	{
		m_indices.push_back(out.trianglelist[i]);
	}

	free(out.pointlist);
	free(out.pointmarkerlist);
	free(out.trianglelist);
	free(out.neighborlist);
	free(out.segmentlist);
	free(out.segmentmarkerlist);
}