/*
 * Copyright (c) 2015 Tricoire Sebastien 3dsman@free.fr
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 * claim that you wrote the original software. If you use this software
 * in a product, an acknowledgment in the product documentation would be
 * appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 * misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 *
 */

#include "stitchs/OE_stitchs.h"
#include "OE_utils.h"
#include "Archive.h"

#include <GL/gl.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <typeinfo>

#include "stitchs/OE_birailstitch.h"

float OE_stitchs::defaultMaxlen = 0;

OE_stitchs::OE_stitchs() : thread(nullptr)
{
}

OE_stitchs::~OE_stitchs()
{
}

void OE_stitchs::persist(Pakal::Archive* archive)
{
	archive->set_type_name("OE_stitchs");
	archive->refer("thread", thread);
	archive->value("maxLen", maxLen);
}

bool OE_stitchs::getPoint(uint16_t nb, float* x, float* y)
{
	if (pts.size() && nb<pts.size())
	{
		*x = pts.at(nb).x;
		*y = pts.at(nb).y;
		return true;
	}
	return false;
}

bool OE_stitchs::getPoint(uint16_t nb, vector_2d* pt)
{
	if (pts.size() && nb<pts.size())
	{
		*pt = pts.at(nb);
		return true;
	}
	return false;
}

std::vector<vector_2d> OE_stitchs::getPoints()
{
	std::vector<vector_2d> out;
	out = pts;
	return out;
}

int OE_stitchs::getNpts()
{
	return pts.size();
}

OE_thread* OE_stitchs::getThread()
{
	return thread;
}

bool OE_stitchs::setPoint(uint16_t nb, float x, float y)
{
	if (nb<pts.size())
	{
		pts.at(nb) = vector_2d(x,y);
		setNeedRefresh();
		return true;
	}
	return false;
}

bool OE_stitchs::setThread(OE_thread* thread)
{
	this->thread = thread;
	setNeedRefresh();
	return true;
}

bool OE_stitchs::addPoint(float x, float y)
{
	return addPoint(vector_2d(x, y));
}

bool OE_stitchs::addPoint(vector_2d pt)
{
	pts.push_back(pt);
	setNeedRefresh();
	return true;
}

bool OE_stitchs::setMaxLen(float maxLen)
{
	this->maxLen = maxLen;
	setNeedRefresh();
	return true;
}

float OE_stitchs::getMaxLen()
{
	return maxLen;
}

#define EPSILON (1e-12)

BoundingBox OE_stitchs::getBound()
{
	bounds.init = false;
	// Find bounds
	for (unsigned i=0; i<pts.size(); i++)
	{
		bounds += pts[i];
	}
	return bounds;
}

bool OE_stitchs::check()
{
	return true;
}

std::vector<vector_2d> OE_stitchs::stitchMaxLen(std::vector<vector_2d> ptsArray, float /*maxlen*/)
{
	std::vector<vector_2d> out;
	if (ptsArray.size())
	{
		out.push_back(ptsArray.at(0));
		for(unsigned i=1; i<ptsArray.size(); i++)
		{
			std::vector<vector_2d> tmpdisc = subd(ptsArray.at(i-1), ptsArray.at(i), maxLen);
			out.insert(out.end(), tmpdisc.begin(), tmpdisc.end());
		}
	}
	return out;
}

void OE_stitchs::stitchPostProcess(std::vector<vector_2d> ptsArray)
{
	pts.clear();

	if(maxLen>0)
	{
		pts = stitchMaxLen(ptsArray, maxLen);
	}
	else
	{
		pts = ptsArray;
	}
}

std::vector<vector_2d> OE_stitchs::warpStitch(std::vector<vector_2d> mainPts, std::vector<vector_2d> normalPts, bool normals,
                                              OE_pattern* pattern, unsigned patternSteps,
                                              float xOffset, float yOffset)
{
	std::vector<vector_2d> outPts;
	vector_2d pt,dir,tmp1,tmp2;
	int xStep;
	unsigned p = 0;

	xOffset *= patternSteps;
	if (mainPts.size() == normalPts.size() && pattern && patternSteps>0)
	{
		if (!normals)
		{

			for(unsigned i=0; i<normalPts.size(); i++)
			{
				normalPts.at(i) = normalPts.at(i)-mainPts.at(i);
			}
		}
		do
		{
			pt = pattern->pts.at(p);
			pt.x = pt.x * patternSteps + xOffset;
			xStep = floor(pt.x);
			pt.x -= xStep;
			if (xStep>=0 && (xStep+1) < mainPts.size())
			{
				dir = mainPts.at(xStep+1)-mainPts.at(xStep);
				pt.y += yOffset;
				tmp1 = normalPts.at(xStep)*pt.y*(1-pt.x)+normalPts.at(xStep+1)*pt.y*pt.x;
				tmp2 = dir*pt.x;
				outPts.push_back(mainPts.at(xStep)+tmp1+tmp2);
			}

			p++;
			if (p >= pattern->pts.size())
			{
				p = 0;
				xOffset += patternSteps;
			}
		} while(xOffset < mainPts.size());
	}
/*
	if ((curvePts.size() == normalPts.size())&&(curvePts.size()>=patternLen))
	{
		for(unsigned i=0; i<curvePts.size()-patternLen; i+=patternLen)
		{
			for(unsigned p = 0;p<pattern->pts.size();p++)
			{
				pt = pattern->pts.at(p);
				pt.x *= patternLen;
				xStep = floor(pt.x);
				pt.x -= xStep;
				dir = curvePts.at(i+xStep+1)-curvePts.at(i+xStep);
				pt.y += offset;
				tmp1 = (normalPts.at(i+xStep)*pt.y)*(1-pt.x)+(normalPts.at(i+xStep+1)*pt.y)*pt.x;
				tmp2 = dir*pt.x;
				outPts.push_back(curvePts.at(i+xStep)+tmp1+tmp2);

			}
		}
	}*/
	return outPts;
}

