/*
 * 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 "OE_document.h"
#include "OE_svgParser.h"
#include "OE_io.h"
#include "stitchs/OE_linestitch.h"
#include "stitchs/OE_birailstitch.h"
#include "stitchs/OE_linkstitch.h"
#include "stitchs/OE_staticstitch.h"
#include "stitchs/OE_fillstitch.h"
#include "Archive.h"
#include "JsonWriter.h"
#include "JsonReader.h"

#include <string>
#include <iostream>
#include <fstream>
#include <map>
#include <limits>
#include <cmath>

OE_document::OE_document()
{
	std::list<OE_pattern*> standard_patterns = OE_pattern::createStandardPatterns();
	patterns.insert(patterns.end(), standard_patterns.begin(), standard_patterns.end());
}

OE_document::~OE_document()
{
}

OE_document::ScopeLock::ScopeLock(OE_document& doc) : std::lock_guard<std::recursive_mutex>(doc.accessLock)
{
}

bool OE_document::saveToFile(std::string path)
{
	ScopeLock lock(*this);
	OE_ofstream out(path);
	if (!out.is_open())
	{
		return false;
	}
	Pakal::JsonWriter json_writer(true);
	json_writer.write(out, "document", *this);
	out.close();
	return true;
}

void OE_document::persist(Pakal::Archive* archive)
{
	//archive->set_type_name("OE_document");
	archive->value("curves", curves);
	archive->value("threads", threads);
	archive->value("stitchs", stitchs);
}

OE_document* OE_document::newFromSvg(const std::string& file)
{
	OE_document* newDoc = OE_svgParser::fromFile(file, "px", 96.0f);
  if (!newDoc)
    return nullptr;
	// Center document around (0,0)
	BoundingBox bb = newDoc->getBound();
	for (auto curve : newDoc->curves)
	{
		curve->move(vector_2d(0,0)-bb.getCenter());
	}
	// Rescale document if larger than hoop
	bb = newDoc->getBound();
	float docW = bb.getMax().x-bb.getMin().x;
	float docH = bb.getMax().y-bb.getMin().y;
	float hoopW = newDoc->getHoopSize().x;
	float hoopH = newDoc->getHoopSize().y;
	if (docW > hoopW || docH > hoopH)
	{
		float ratio = nanf("");
		if (docW >= docH && docW != 0.0f)
		{
			ratio = hoopW / (docW);
		}
		else if (docW < docH && docH != 0.0f)
		{
			ratio = hoopH / (docH);
		}
		if (!std::isnan(ratio))
		{
			for (auto curve : newDoc->curves)
			{
				curve->scale(vector_2d(ratio, ratio), bb.getCenter());
			}
		}
	}
	return newDoc;
}


bool OE_document::loadFromFile(std::string path)
{
	ScopeLock lock(*this);
	OE_ifstream in(path);
	if (!in.is_open())
	{
		return false;
	}
	Pakal::SimpleFactoyManager manager;
	manager.declare_object<OE_birailstitch>("OE_birailstitch", []() { return new OE_birailstitch(nullptr); });
	manager.declare_object<OE_linestitch>("OE_linestitch", []() { return new OE_linestitch(); });
	manager.declare_object<OE_linkstitch>("OE_linkstitch", []() { return new OE_linkstitch(); });
	manager.declare_object<OE_fillstitch>("OE_fillstitch", []() { return new OE_fillstitch(); });
	manager.declare_object<OE_staticstitch>("OE_staticstitch", []() { return new OE_staticstitch(); });
	Pakal::JsonReader json_reader(&manager);
	json_reader.read(in, "document", *this);
	in.close();
	for (auto& stitch : stitchs)
	{
		stitch->refreshDependency();
	}
	return true;
}

void OE_document::lock()
{
	accessLock.lock();
}

void OE_document::unlock()
{
	accessLock.unlock();
}

bool OE_document::trylock()
{
	return accessLock.try_lock();
}

BoundingBox OE_document::getBound()
{
	BoundingBox box;
	if (curves.size())
	{
		std::list<OE_pointcurve*>::iterator curve = curves.begin();
		box += (*curve)->getBound();
		curve++;
		
		while (curve != curves.end())
		{
		//for (unsigned i=0; i<curves.size(); i++)
		//{
			box += (*curve)->getBound();
			/*
			 (*curve)->getBound(&tmpXMin, &tmpYMin, &tmpXMax, &tmpYMax);
			 *xMin = minf(*xMin,tmpXMin);
			 *yMin = minf(*yMin,tmpYMin);
			 *xMax = maxf(*xMax,tmpXMax);
			 *yMax = maxf(*yMax,tmpYMax);*/
			curve++;
		}
		
		std::list<OE_stitchs*>::iterator stitch = stitchs.begin();
		
		while (stitch != stitchs.end())
		{
		//for (unsigned i=0; i<stitchs.size(); i++)
		//{
			box += (*stitch)->getBound();
			/*
			 (*stitch)->getBound(&tmpXMin, &tmpYMin, &tmpXMax, &tmpYMax);
			 *xMin = minf(*xMin,tmpXMin);
			 *yMin = minf(*yMin,tmpYMin);
			 *xMax = maxf(*xMax,tmpXMax);
			 *yMax = maxf(*yMax,tmpYMax);*/
			stitch++;
		 }
	}
	else
	{
		box += BoundingBox(-hoopSize.x/2, -hoopSize.y/2, hoopSize.x/2, hoopSize.y/2);
	}
	return box;
}

vector_2d OE_document::getHoopSize()
{
	return hoopSize;
}

unsigned int OE_document::getPulseByMm()
{
	return pulseByMm;
}

vector_2d OE_document::getZeroPoint()
{
	return zeroPoint;
}

void OE_document::setHoopSize(vector_2d hoopSize)
{
	this->hoopSize = hoopSize;
}

void OE_document::setPulseByMm(unsigned int pbm)
{
	pulseByMm = pbm;
}

void OE_document::setZeroPoint(vector_2d zeroPoint)
{
	this->zeroPoint = zeroPoint;
}


bool OE_document::addCurve(OE_pointcurve* curve)
{
	curves.push_back(curve);
	return true;
}

bool OE_document::addStitch(OE_stitchs* stitch)
{
	stitchs.push_back(stitch);
	return true;
}

bool OE_document::addThread(OE_thread* thread)
{
	threads.push_back(thread);
	return true;
}


void OE_document::removeCurve(OE_pointcurve* curve)
{
	selectedCurves.remove(curve);
	curves.remove(curve);
}

void OE_document::removeStitch(OE_stitchs* stitch)
{
	selectedStitchs.remove(stitch);
	stitchs.remove(stitch);
}

void OE_document::removeThread(OE_thread* thread)
{
	threads.remove(thread);
}


bool OE_document::refresh()
{
	return true;
}

/*
bool OE_document::draw(float dpi)
{
	for (unsigned i=0; i<curves.size(); i++)
	{
		curves.at(i).draw(dpi);
	}
	for (unsigned i=0; i<stitchs.size(); i++)
	{
		stitchs.at(i)->draw();
	}
	return true;
}*/

extern const OE_io ioPes;

bool OE_document::loadFromPES(std::string path)
{
	ScopeLock lock(*this);
	(*ioPes.loadFromFile)(path, this);
	for (auto& stitch : stitchs)
	{
		stitch->refreshDependency();
	}
	return true;
}

