/*
* This file is part of project OpenEmbroidery. It's copyrighted by
* the contributors recorded in the version control history of the file.
* Original project location https://code.electrolab.fr/openEmbroidery/openEmbroidery_software
*
* SPDX-License-Identifier: CECILL-2.1
* License-Filename: Licence_CeCILL_V2.1-en.txt
*/

#include "stitchs/OE_fillstitch.h"
#include "curves/OE_pointcurve.h"
#include "curves/OE_joincurve.h"
#include "curves/OE_subcurve.h"
#include "OE_utils.h"
#include <iostream>
#include <stdio.h>
#include <algorithm>

void OE_fillstitch::contour_segment::addIntersection(OE_fillstitch::intersection intersection)
{
	intersections.push_back(intersection);
	/*if (!intersection.cross)
	{
		return;
	}*/
	if (minXCell == -1)
	{
		if (intersection.xcross)
		{
			minXCell = floorf(intersection.gridXPos);
			maxXCell = floorf(intersection.gridXPos+1);
			minYCell = intersection.gridYPos;
			maxYCell = intersection.gridYPos;
		}else
		{
			minXCell = intersection.gridXPos;
			maxXCell = intersection.gridXPos;
			minYCell = floorf(intersection.gridYPos);
			maxYCell = floorf(intersection.gridYPos+1);
		}
	}
	else
	{
		if (intersection.xcross)
		{
			minXCell = std::min(minXCell, (int)floorf(intersection.gridXPos));
			maxXCell = std::max(maxXCell, (int)floorf(intersection.gridXPos+1));
			minYCell = std::min(minYCell, (int)intersection.gridYPos);
			maxYCell = std::max(maxYCell, (int)intersection.gridYPos);
		}else
		{
			minXCell = std::min(minXCell, (int)intersection.gridXPos);
			maxXCell = std::max(maxXCell, (int)intersection.gridXPos);
			minYCell = std::min(minYCell, (int)floorf(intersection.gridYPos));
			maxYCell = std::max(maxYCell, (int)floorf(intersection.gridYPos+1));
		}
	}
}

void OE_fillstitch::stitch_line::addIntersection(intersection intersection)
{
	intersections.push_back(intersection);
}

OE_fillstitch::fillPatch::fillPatch() : minXCell(-1)
{
	invertRow = false;
	invertCol = false;
}

void OE_fillstitch::fillPatch::addSegment(contour_segment _segment, unsigned _id)
{
	contour_segments.push_back(_id);
	if (minXCell == -1)
	{
		minXCell = _segment.minXCell;
		minYCell = _segment.minYCell;
		maxXCell = _segment.maxXCell;
		maxYCell = _segment.maxYCell;
	}
	else
	{
		minXCell = std::min(minXCell, _segment.minXCell);
		minYCell = std::min(minYCell, _segment.minYCell);
		maxXCell = std::max(maxXCell, _segment.maxXCell);
		maxYCell = std::max(maxYCell, _segment.maxYCell);
	}
}

void OE_fillstitch::fillPatch::addCut(int _cut)
{
	cuts.push_back(_cut);
}

OE_fillstitch::cut::cut(int _intersect1, int _intersect2) : contours(4, -1)
{
	intersect1 = _intersect1;
	intersect2 = _intersect2;
}

OE_fillstitch::OE_fillstitch() : curve(nullptr), reverse(false), len(1), width(1), pattern(nullptr), xOffset(0), initGrid(true)
{
	maxLen = 5;
	curveControl.resize(4);
	for(int i=0;i<4;i++)
	{
		curveControl[i].addPoint(vector_2d(0,0));
		curveControl[i].addPoint(vector_2d(0,0));
		curveControl[i].addPoint(vector_2d(0,0));
		curveControl[i].addPoint(vector_2d(0,0));
	}
	setNeedRefresh();
}

OE_fillstitch::OE_fillstitch(OE_thread* thread, float len, float width, OE_pattern* pattern)
{
	curve = new OE_joincurve();
	curve->addDependency(this);
	curve->setClosed(true);
	this->len = len;
	this->width = width;
	this->thread = thread;
	this->pattern = pattern;
	this->xOffset = 0;
	this->maxLen = 5;

	initGrid = true;

	curveControl.resize(4);
	for(int i=0;i<4;i++)
	{
		curveControl[i].addPoint(vector_2d(0,0));
		curveControl[i].addPoint(vector_2d(0,0));
		curveControl[i].addPoint(vector_2d(0,0));
		curveControl[i].addPoint(vector_2d(0,0));
	}
	setNeedRefresh();
}

void OE_fillstitch::persist(Pakal::Archive* archive)
{
	OE_stitchs::persist(archive);

	archive->set_type_name("OE_fillstitch");
	archive->value("curve", curve);
	archive->value("reverse", reverse);
	archive->value("len", len);
	archive->value("width", width);
	archive->value("pattern", pattern);
	archive->value("xOffset", xOffset);
	archive->value("gridControl", gridControl);
}


OE_fillstitch::~OE_fillstitch()
{
	std::list<OE_subcurve*>::iterator i = curve->curves.begin();
	 while (i != curve->curves.end())
	 {
		 (*i)->removeDependency(curve);
		 delete *i;
		 i++;
	 }
	if (curve)
	{
		delete curve;
	}
}

/*
void OE_fillstitch::setCurve(OE_curve* curve)
{
	if (this->curve)this->curve->removeDependency(this);
	this->curve = curve;
	curve->addDependency(this);
	setNeedRefresh();
}
*/

void OE_fillstitch::setLen(float len)
{
	this->len = len;
	setNeedRefresh();
}
void OE_fillstitch::setWidth(float width)
{
	this->width = width;
	setNeedRefresh();
}
void OE_fillstitch::setPattern(OE_pattern* stitch)
{
	pattern=stitch;
	setNeedRefresh();
}

OE_joincurve* OE_fillstitch::getJoincurve()
{
	return curve;
}

std::vector<vector_2d> OE_fillstitch::getGridControl()
{
	return gridControl;
}

std::vector<OE_pointcurve> OE_fillstitch::getCurveControl()
{
	return curveControl;
}

vector_2d OE_fillstitch::getGridControlPoint(int index)
{
	if (index < gridControl.size())
	{
		return gridControl[index];
	}else
	{
		return vector_2d(0,0);
	}
}

void OE_fillstitch::setGridControlPoint(int index, vector_2d point)
{

	if (index<gridControl.size())
	{
		vector_2d offset = point - gridControl[index];
		if (index == 0)
		{
			gridControl[1] += offset;
			gridControl[4] += offset;
		}
		if (index == 3)
		{
			gridControl[2] += offset;
			gridControl[6] += offset;
		}
		if (index == 8)
		{
			gridControl[5] += offset;
			gridControl[9] += offset;
		}
		if (index == 11)
		{
			gridControl[7] += offset;
			gridControl[10] += offset;
		}
		gridControl[index] = point;
		refreshCurves();

	}
}

void OE_fillstitch::move(vector_2d offset)
{
	for (unsigned i=0; i<gridControl.size(); i++)
	{
		gridControl[i] = gridControl[i] + offset;
	}
	refreshCurves();
}

void OE_fillstitch::scale(vector_2d ratio, vector_2d pos)
{
	for (unsigned i=0; i<gridControl.size(); i++)
	{
		gridControl[i] = pos + (gridControl[i] - pos)*ratio;
	}
	refreshCurves();
}

float OE_fillstitch::getLen()
{
	return len;
}
float OE_fillstitch::getWidth()
{
	return width;
}
OE_pattern* OE_fillstitch::getPattern()
{
	return pattern;
}

bool OE_fillstitch::check()
{
	return curve && curve->check() && pattern && len;
}

void OE_fillstitch::initGridControl()
{

	BoundingBox bound;
	vector_2d min;
	vector_2d max;

	bound = curve->getBound();

	min = bound.getMin();
	max = bound.getMax();
	min = min + vector_2d(-1, -1);
	max = max + vector_2d(1, 1);

	gridControl.resize(12);


	gridControl[0]=min;
	gridControl[1]=vector_2d(min.x+(max.x-min.x)/3, min.y-20);
	gridControl[2]=vector_2d(min.x+2*(max.x-min.x)/3, min.y+1);
	gridControl[3]=vector_2d(max.x, min.y);

	gridControl[8]=vector_2d(min.x, max.y);
	gridControl[9]=vector_2d(min.x+(max.x-min.x)/3, max.y+5);
	gridControl[10]=vector_2d(min.x+2*(max.x-min.x)/3, max.y+12);
	gridControl[11]=max;

	gridControl[4]=(gridControl[8]-gridControl[0])/3+gridControl[0];
	gridControl[5]=(2*(gridControl[8]-gridControl[0]))/3+gridControl[0]+vector_2d(-5,0);
	gridControl[6]=(gridControl[11]-gridControl[3])/3+gridControl[3];
	gridControl[7]=(2*(gridControl[11]-gridControl[3]))/3+gridControl[3]+vector_2d(+5,0);

	refreshCurves();

}

void OE_fillstitch::refreshCurves()
{

	curveControl[0].setPoint(1,gridControl[0]);
	curveControl[0].setPoint(2,gridControl[1]);
	curveControl[0].setPoint(3,gridControl[2]);
	curveControl[0].setPoint(4,gridControl[3]);

	curveControl[1].setPoint(1,gridControl[8]);
	curveControl[1].setPoint(2,gridControl[9]);
	curveControl[1].setPoint(3,gridControl[10]);
	curveControl[1].setPoint(4,gridControl[11]);

	curveControl[2].setPoint(1,gridControl[0]);
	curveControl[2].setPoint(2,gridControl[4]);
	curveControl[2].setPoint(3,gridControl[5]);
	curveControl[2].setPoint(4,gridControl[8]);

	curveControl[3].setPoint(1,gridControl[3]);
	curveControl[3].setPoint(2,gridControl[6]);
	curveControl[3].setPoint(3,gridControl[7]);
	curveControl[3].setPoint(4,gridControl[11]);
	setNeedRefresh();
}

std::vector<std::vector<vector_2d>> OE_fillstitch::generateGrid(float precision)
{
	if (initGrid) initGridControl();
	initGrid = false;

	OE_pointcurve curve1 = curveControl[0];
	OE_pointcurve curve2 = curveControl[1];
	OE_pointcurve curve3 = curveControl[2];
	OE_pointcurve curve4 = curveControl[3];
	std::vector<std::vector<vector_2d>> gridPts;

	float curve1Len = curve1.getLength(precision);
	float curve2Len = curve2.getLength(precision);
	float curve3Len = curve3.getLength(precision);
	float curve4Len = curve4.getLength(precision);
	gridXSize = (curve1Len+curve2Len)/2/len;
	gridYSize = (curve3Len+curve4Len)/2/width;

	if (!gridXSize||!gridXSize)
	{
		return gridPts;
	}

	std::cout<<"gridSize: "<<gridXSize<<" "<<gridYSize<<std::endl;
	std::vector<vector_2dt> curve1Pts;
	std::vector<vector_2dt> curve2Pts;
	curve1Pts = curve1.discretizeRegular(curve1Len/gridXSize);
	curve2Pts = curve2.discretizeRegular(curve2Len/gridXSize);
	std::vector<vector_2dt> curve3Pts;
	std::vector<vector_2dt> curve4Pts;
	curve3Pts = curve3.discretizeRegular(curve3Len/gridYSize);
	curve4Pts = curve4.discretizeRegular(curve4Len/gridYSize);

	gridPts.resize(gridXSize+1);
	for (unsigned i=0; i<gridXSize+1; ++i)
	{
		gridPts[i].resize(gridYSize+1);
	}

	vector_2d pt;
	float iStep = 1.0f/gridXSize;
	float jStep = 1.0f/gridYSize;
	float ifloat, jfloat;
	for(unsigned i=0; i<=gridXSize; i++)
	{
		ifloat = iStep*i;
		for(unsigned j=0; j<=gridYSize; j++)
		{
			jfloat = jStep*j;
			gridPts[i][j] = vector_2d(curve1Pts.at(i)+(curve2Pts.at(i)-curve1Pts.at(i))/gridYSize*j+
							 curve3Pts.at(j)+(curve4Pts.at(j)-curve3Pts.at(j))/gridXSize*i)-
					((1-ifloat)*(1-jfloat)*curve1Pts.at(0)+(1-ifloat)*jfloat*curve2Pts.at(0)+(1-jfloat)*ifloat*curve1Pts.at(gridXSize)+ifloat*jfloat*curve2Pts.at(gridXSize));
		}
	}
	return gridPts;
}

bool OE_fillstitch::findIntersections(const std::vector<vector_2dt> curvePts, const std::vector<std::vector<vector_2d>> gridPts, std::vector<OE_fillstitch::intersection> &intersections, std::vector<int> &inflexions)
{
	// must find and init to the start cell
	int curXCell=0;
	int curYCell=0;
	bool out = false;
	do
	{
		curYCell = 0;
		do
		{
			//we check every cell to find the one where the first curve point is
			out = (PointInTriangle(curvePts[0],
								   gridPts[curXCell+1][curYCell],
								   gridPts[curXCell][curYCell],
								   gridPts[curXCell][curYCell+1]) ||
				   PointInTriangle(curvePts[0],
								   gridPts[curXCell+1][curYCell],
								   gridPts[curXCell][curYCell+1],
								   gridPts[curXCell+1][curYCell+1]));
			curYCell++;
		} while (curYCell<gridYSize && !out);
		curXCell++;
	} while (curXCell<gridXSize && !out);
	curXCell--;
	curYCell--;

	// find every intersection between the curve and the grid
	char dir = 0;

	bool intersected = false;
	int prevP = curvePts.size()-1;

	vector_2d intersectPt;
	float intersectPosGrid = 0;
	float intersectPosCurve;
	bool up = false;

	for(std::vector<vector_2dt>::size_type p = 0; p < curvePts.size(); p++)
	{
		intersections.push_back(intersection(curvePts[prevP].v, false, false, curYCell, prevP, curXCell));
		do
		{
			intersected = false;
			// check if the segment get out the cell and change the cell in consequence
			// dir store the incoming direction of the segment in a cell. That reduce testing and avoid double detection
			if (dir!=1 && intersect(curvePts[prevP].v, curvePts[p].v,
			                        gridPts[curXCell][curYCell],
			                        gridPts[curXCell][curYCell+1],
			                        intersectPt, intersectPosCurve, intersectPosGrid))
			{
				//intersections.push_back(intersection(intersectPt, true, false, curYCell + intersectPosCurve, p, curXCell));
				dir = 2;
				curXCell--;
				if (curXCell<0)
				{
					return false;
				}
				intersected = true;
			}
			if (dir!=2 && intersect(curvePts[prevP].v, curvePts[p].v,
			                        gridPts[curXCell+1][curYCell], gridPts[curXCell+1][curYCell+1],
			                        intersectPt, intersectPosCurve, intersectPosGrid))
			{
				curXCell++;
				if (curXCell>=gridXSize)
				{
					return false;
				}
				//intersections.push_back(intersection(intersectPt, true, false, curYCell + intersectPosCurve, p, curXCell));
				dir = 1;
				intersected = true;
			}
			if (dir!=3 && intersect(curvePts[prevP].v, curvePts[p].v,
			                        gridPts[curXCell][curYCell], gridPts[curXCell+1][curYCell],
			                        intersectPt, intersectPosCurve, intersectPosGrid))
			{
				intersections.push_back(intersection(intersectPt, true, true, curYCell, p, curXCell + intersectPosGrid));

				if (up)
				{
					// if needed we store inflexion point local maximum here
					inflexions.push_back(intersections.size()-1);
				}
				up = false;
				dir = 4;
				curYCell--;
				if (curYCell<0)
				{
					return false;
				}
				intersected = true;
			}
			if (dir!=4 && intersect(curvePts[prevP].v, curvePts[p].v,
			                        gridPts[curXCell][curYCell+1], gridPts[curXCell+1][curYCell+1],
			                        intersectPt, intersectPosCurve, intersectPosGrid))
			{
				curYCell++;
				if (curYCell>=gridYSize)
				{
					return false;
				}
				intersections.push_back(intersection(intersectPt, true, true, curYCell, p, curXCell + intersectPosGrid));

				if (!up)
				{
					// and local minimum here
					inflexions.push_back(intersections.size()-1);
				}
				up = true;
				dir = 3;
				intersected = true;
			}
		} while (intersected);
		dir = 0;
		prevP = p;
	}

	if (intersections.size() == 0)
	{
		return false;
	}
	return true;
}

bool OE_fillstitch::findCuts( std::vector<int> inflexions, std::vector<OE_fillstitch::intersection> &intersections, std::vector<cut> &cuts)
{
	int nbCross = 0;
	int closestLeft = -1;
	int closestRight = -1;

	//for each inflexion point i will find closest intersection on the same grid line
	for(int inflex : inflexions)
	{
		closestLeft = -1;
		closestRight = -1;
		int prevInflex;
		int prev = inflex;
		prevInflex = prev;

		//find the previous point that cross the same X grid line
		do
		{
			if (prevInflex == 0)
				prevInflex = intersections.size();
			prevInflex--;
			if(prevInflex==prev) return false;
		}
		while (intersections[prevInflex].xcross != true ||
		       intersections[prevInflex].gridYPos != intersections[inflex].gridYPos);

		for(std::vector<intersection>::size_type p = 0; p < intersections.size(); p++)
		{
			//check every intersection to find those who are on the same grid line
			if (intersections[p].xcross == true &&
				intersections[p].gridYPos == intersections[inflex].gridYPos &&
				p != inflex &&
				p != prevInflex)
			{
				// check if they are before or after the inflexion point, get the closest and count the one that are at left
				if (intersections[p].gridXPos < intersections[inflex].gridXPos)
				{
					if (closestLeft == -1 || intersections[closestLeft].gridXPos < intersections[p].gridXPos)
					{
						closestLeft = p;
					}
					nbCross++;
				}
				else
				{
					if (closestRight == -1 || intersections[closestRight].gridXPos > intersections[p].gridXPos)
					{
						closestRight = p;
					}
				}
			}
		}
		// if we didn't cross the curve twice, we're inside the curve
		if (nbCross%2 == 1)
		{
			//we add then the 2 cuts to the cut list and set the cutIndex to the intersections
			if (intersections[prevInflex].gridXPos < intersections[inflex].gridXPos)
			{

				intersections[closestLeft].cutIndex = cuts.size();
				intersections[prevInflex].cutIndex = cuts.size();
				cuts.push_back(cut(closestLeft,prevInflex));

				intersections[inflex].cutIndex = cuts.size();
				intersections[closestRight].cutIndex = cuts.size();
				cuts.push_back(cut(inflex,closestRight));
			}
			else
			{
				intersections[closestLeft].cutIndex = cuts.size();
				intersections[inflex].cutIndex = cuts.size();
				cuts.push_back(cut(closestLeft,inflex));

				intersections[prevInflex].cutIndex = cuts.size();
				intersections[closestRight].cutIndex = cuts.size();
				cuts.push_back(cut(prevInflex,closestRight));
			}
		}
		nbCross = 0;
	}
	return true;
}

bool OE_fillstitch::findPatchs(const std::vector<OE_fillstitch::intersection> intersections, std::vector<cut> &cuts, std::vector<contour_segment> &segments, std::vector<fillPatch> &patchs)
{

	int startIntersect=0, curIntersect=0, curSegment = 0, curcut=0, curpatch = 0;

	std::list<int> patchsStack;
	//std::list<int> cutStack;
	patchs.push_back(fillPatch());
	patchsStack.push_front(0);

	if (!cuts.size())
	{
		while (curIntersect<intersections.size())
		{
			segments[0].addIntersection(intersections[curIntersect]);
			curIntersect++;
		}

		segments[0].addIntersection(segments[0].intersections[0]);
		patchs[0].addSegment(segments[0], 0);
		return true;
	}


	//loop to start at the first cut
	while (intersections[startIntersect].cutIndex == -1 && startIntersect<intersections.size())
	{
		startIntersect++;
	}
	curcut = intersections[startIntersect].cutIndex;
	//set the cutStart of the first segment with the first cut index
	segments[0].cutStart = curcut;
	//cutStack.push_front(curcut);
	cuts[curcut].contours[0] = curSegment;

	//add the first intersection to the first segment
	segments[curSegment].addIntersection(intersections[startIntersect]);
	curIntersect = startIntersect;


	//loop on every intersections
	do{

		//increment the current intersection
		curIntersect = (curIntersect+1)%intersections.size();

		//add the curent intersection to the current segment
		segments[curSegment].addIntersection(intersections[curIntersect]);
		//if we find a cut
		if (intersections[curIntersect].cutIndex!=-1)
		{
			//we set it as current
			curcut = intersections[curIntersect].cutIndex;

			//we close the current segment by setting cutEnd and add it to the current patch
			segments[curSegment].cutEnd = curcut;
			patchs[curpatch].addSegment(segments[curSegment], segments.size()-1);

			if ((curIntersect != startIntersect))
			{
				//create a new segment
				segments.push_back(contour_segment());
				curSegment++;

				segments[curSegment].cutStart = curcut;

				//add the current intersection to the new segment
				segments[curSegment].addIntersection(intersections[curIntersect]);

				// if we already marked this cut we can pop the patch out of the stack and mark contours indexes
				if (cuts[curcut].contours[0]!=-1)
				{
					cuts[curcut].contours[1] = curSegment-1;
					cuts[curcut].contours[3] = curSegment;
					patchsStack.pop_front();

					// if the stack is empty we start a new pach
					if (patchsStack.empty())
					{
						patchs.push_back(fillPatch());
						patchsStack.push_front(patchs.size()-1);
					}
				}
				else
				{
					//else it's the first contact with this cut so we mark his first contours index
					cuts[curcut].contours[0] = curSegment;
					cuts[curcut].contours[2] = curSegment-1;
					//then init a new patch
					patchs.push_back(fillPatch());
					patchsStack.push_front(patchs.size()-1);
				}
				curpatch = patchsStack.front();
			}
			else
			{
				//if we finished the loop we just need to set the last cut contour
				cuts[curcut].contours[2] = curSegment-1;
			}
		}

	}while (curIntersect!=startIntersect);

	return true;
}

bool OE_fillstitch::generateStitchLines(std::vector<contour_segment> segments, fillPatch &patch)
{
	patch.stitch_lines.resize(std::max(1, patch.maxYCell-patch.minYCell));

	std::cout<<"lines "<<patch.stitch_lines.size()<< "  " <<patch.minYCell<<" "<<patch.maxYCell<<std::endl;
	//for each contour segment of the patch
	for(std::vector<contour_segment>::size_type segment = 0; segment < patch.contour_segments.size(); segment++)
	{
		const contour_segment curSegment = segments[patch.contour_segments[segment]];
		std::cout<<"segment ID "<< patch.contour_segments[segment]<<" "<<curSegment.minYCell<<" "<<curSegment.maxYCell <<std::endl;
		//std::cout<<" intersections "<<curSegment.intersections.size()<<std::endl;
		//for each intersection in the current segment
		for(std::vector<intersection>::size_type p = 0; p < curSegment.intersections.size(); p++)
		{
			std::cout<<"  intersection Ypos "<<curSegment.intersections[p].gridYPos<<std::endl;
			int yCell = floor(curSegment.intersections[p].gridYPos);
			//stitch_line_id is the index of the line into this patch (relative to the minYcell of the patch)
			int stitch_line_id = yCell - patch.minYCell;
			float xCell = curSegment.intersections[p].gridXPos;

			//if we add an intersection to the right line
			if (stitch_line_id<patch.stitch_lines.size())
			{

				stitch_line* curStitch_line = &(patch.stitch_lines[stitch_line_id]);
				curStitch_line->yCell = yCell;
				curStitch_line->addIntersection(curSegment.intersections[p]);
				std::cout<<"  lineID "<< stitch_line_id<<"  xcell "<<xCell<<"  ycell "<<yCell<<std::endl;

				if ((curStitch_line->minXCell>ceil(xCell-1))||(curStitch_line->minXCell == -1))
				{
					curStitch_line->minXCell=ceil(xCell-1);
				}
				if ((curStitch_line->maxXCell<floor(xCell+1))||(curStitch_line->maxXCell == -1))
				{
					curStitch_line->maxXCell=floor(xCell+1);
				}
			}

			//if this intersection is an xcross we need to add it to the previous line too
			if ((curSegment.intersections[p].xcross)&&(stitch_line_id > 0))
			{
				std::cout<<"  xcross "<<stitch_line_id-1<<std::endl;
				stitch_line* prevStitch_line = &(patch.stitch_lines[stitch_line_id-1]);
				prevStitch_line->yCell = yCell-1;
				prevStitch_line->addIntersection(curSegment.intersections[p]);
				if ((prevStitch_line->minXCell>floor(xCell))||(prevStitch_line->minXCell == -1))
				{
					prevStitch_line->minXCell=floor(xCell);
				}
				if ((prevStitch_line->maxXCell<ceil(xCell))||(prevStitch_line->maxXCell == -1))
				{
					prevStitch_line->maxXCell=ceil(xCell);
				}
			}

		}
		//std::cout<<"segmentID "<< patch.contour_segments[segment]<<" ok"<<std::endl;
	}
	//check if results is consistent (can be incorrect in case of self intersecting form)

	for(std::vector<stitch_line>::size_type curLine = 0; curLine < patch.stitch_lines.size(); curLine++)
	{
		if ((patch.stitch_lines[curLine].minXCell<0)||(patch.stitch_lines[curLine].maxXCell<0))
		{
			std::cout<<"  fail "<<patch.stitch_lines.size()<< " "<<curLine<<"  "<<patch.stitch_lines[curLine].minXCell<< "  " <<patch.stitch_lines[curLine].maxXCell<<std::endl;
			return false;
		}
	}
	return true;
}

bool OE_fillstitch::generateStitches( const std::vector<std::vector<vector_2d>> gridPts, fillPatch &patch)
{
	std::vector<vector_2d> linePts;
	stitch_line line;
	bool invertRow = patch.invertRow;

	for(std::vector<stitch_line>::size_type curLine = 0; curLine < patch.stitch_lines.size(); curLine++)
	{
		line = patch.stitch_lines[curLine];
//------------------------------------  warp lines
		std::vector<vector_2d> mainPts;
		std::vector<vector_2d> normalPts;
		std::vector<vector_2d> linePts;
		mainPts.reserve(line.maxXCell-line.minXCell+3);
		normalPts.reserve(line.maxXCell-line.minXCell+3);
		linePts.reserve((line.maxXCell-line.minXCell+3)*pattern->pts.size());

		mainPts.push_back(2*gridPts[line.minXCell][line.yCell]-gridPts[line.minXCell+1][line.yCell]);
		normalPts.push_back(2*gridPts[line.minXCell][line.yCell+1]-gridPts[line.minXCell+1][line.yCell+1]);
		for(int j=line.minXCell; j<=line.maxXCell; j++)
		{
			mainPts.push_back(gridPts[j][line.yCell]);
			normalPts.push_back(gridPts[j][line.yCell+1]);
		}
		mainPts.push_back(2*gridPts[line.maxXCell][line.yCell]-gridPts[line.maxXCell-1][line.yCell]);
		normalPts.push_back(2*gridPts[line.maxXCell][line.yCell+1]-gridPts[line.maxXCell-1][line.yCell+1]);
		//linePts = normalPts;
		linePts = warpStitch(mainPts, normalPts, false, pattern, 1, (line.yCell*xOffset)-ceil(line.yCell*xOffset), 0);

		if (invertRow)
		{
			std::reverse(linePts.begin(), linePts.end());
		}

		patch.patchStitches.insert(patch.patchStitches.end(), linePts.begin(), linePts.end());
		linePts.clear();

		invertRow = !invertRow;
	}
	if (patch.invertCol)
	{
		std::reverse(patch.patchStitches.begin(), patch.patchStitches.end());
	}
	return true;
}

bool OE_fillstitch::prolongateSegments(std::vector<contour_segment> &segments)
{
	for(std::vector<contour_segment>::size_type segment = 0; segment < segments.size(); segment++)
	{
		int size = segments[segment].intersections.size();
		segments[segment].intersections[0].pt = segments[segment].intersections[0].pt+segments[segment].intersections[0].pt-segments[segment].intersections[1].pt;
		segments[segment].intersections[size-1].pt = segments[segment].intersections[size-1].pt+segments[segment].intersections[size-1].pt-segments[segment].intersections[size-2].pt;
	}
	return true;
}


bool OE_fillstitch::cutStitches( const std::vector<contour_segment> segments, fillPatch &patch)
{
	std::vector<vector_2d> tmpPatchStitches;
	//------------------------------- cut patchs
	tmpPatchStitches.reserve(patch.patchStitches.size());
	bool inside = false;
	vector_2d intersectPt;
	vector_2d tmpIntersectPt;
	vector_2d *prevPt;
	vector_2d *curPt;
	float oldIntersectPosStitch = 0, intersectPosStitch = 0, intersectPosContour;
	std::list<vector_2d> cuts;
	//std::cout<<"pwet "<<patch.patchStitches.size()<<std::endl;

	prevPt = &patch.patchStitches[0];
	curPt = prevPt+1;
	unsigned int i;
	i = 0;
	//std::vector<contour_segment>::size_type segment;
	unsigned int segment;
	segment = 0;
	unsigned int curSegment;
	//const fillPatch * patch = &patchs[curPatch];

	vector_2d curSegmenPt;
	vector_2d prevSegmenPt;

	//loop every stitch points
	while (curPt<&patch.patchStitches[patch.patchStitches.size()])
	{
		//loop every segments of the curent patch
		while ((segment < patch.contour_segments.size()))
		{
			curSegment = patch.contour_segments[segment];

			prevSegmenPt = segments[curSegment].intersections[0].pt;
			//loop every intersection of the curent segment
			while(i < segments[curSegment].intersections.size()-1)
			{
				//look for real contour points in the segment intersections array
				do{
					i++;
				}while((i < segments[curSegment].intersections.size()-1)&&(segments[curSegment].intersections[i].cross));
				curSegmenPt = segments[curSegment].intersections[i].pt;

				//test the intersection and eventualy store it to the cuts list at the right position
				if (intersect(*prevPt, *curPt, prevSegmenPt, curSegmenPt,
				              intersectPt, intersectPosStitch, intersectPosContour, true))
				{
					if (oldIntersectPosStitch<intersectPosStitch)
					{
						cuts.push_back(intersectPt);
					}else
					{
						cuts.push_front(intersectPt);
					}
					oldIntersectPosStitch = intersectPosStitch;
				}

				prevSegmenPt = curSegmenPt;
			}

			i=0;
			segment++;
		};

		// add intersections to the temp patch stitches
		for(vector_2d pt : cuts)
		{
			inside = !inside;
			tmpPatchStitches.push_back(pt);
		}
		cuts.clear();

		//add endpoint too if it's inside
		if (inside)
		{
			tmpPatchStitches.push_back(*curPt);
		}

		// goto the next stitch point and reset the tested contour segment
		prevPt = curPt;
		curPt++;
		segment = 0;
	}

	// replace the uncuted patch stitches by the cleanup one
	patch.patchStitches.clear();
	patch.patchStitches.insert(patch.patchStitches.end(), tmpPatchStitches.begin(), tmpPatchStitches.end());
	return true;
}


void OE_fillstitch::refresh(float dpi, bool force)
{
	std::vector<vector_2dt> curvePts;
	std::vector<vector_2dt> prevline;
	std::vector<vector_2dt> curline;

	std::vector<std::vector<vector_2d>> gridPts;
	std::vector<intersection> intersections;
	std::vector<int> inflexion;
	std::vector<cut> cuts;
	std::vector<contour_segment> segments (1);
	std::vector<fillPatch> patchs;

	std::vector<vector_2d> outPts;

	if (!check() || !(needRefresh && force))
	{
		return;
	}

	//curve->setClosed(true);
	curve->refresh( dpi);
	if (curve->getNpts()<=3)
	{
		return;
	}

	curvePts = curve->discretizeFast(0.1f);
	curvePts.push_back(curvePts[0]);

	if (curvePts.size()<2)
	{
		std::cout << "curve discretisation fail" << std::endl;
		return;
	}

	gridPts = generateGrid(dpi);
	if (!gridPts.size())
	{
		std::cout << "grid generation fail" << std::endl;
		return;
	}


	/*for(unsigned i=0; i<=gridYSize; i++)
	{
		for(unsigned j=0; j<=gridXSize; j++)
		{
			outPts.push_back(gridPts[j][i]);
		}
	}*/
pts = outPts;
	std::cout << "findIntersections" << std::endl;
	if (!findIntersections(curvePts, gridPts, intersections, inflexion))
	{
		std::cout << "problem in findIntersections" << std::endl;
		return;
	}
	std::cout << "findCuts" << std::endl;
	if (!findCuts(inflexion, intersections, cuts))
	{
		std::cout << "problem in findCuts" << std::endl;
		return;
	}

	std::cout << "findPatchs" << std::endl;
	if (!findPatchs( intersections, cuts, segments, patchs))
	{
		std::cout << "problem in findPatchs" << std::endl;
		return;
	}

	std::cout << "generateStitchLines" << std::endl;
	for(std::vector<fillPatch>::size_type curPatch = 0; curPatch < patchs.size(); curPatch++)
	{
		if (!generateStitchLines( segments, patchs[curPatch]))
		{
			std::cout << "problem in generateStitchLines" << std::endl;
			return;
		}
		std::cout << "generateStitchLines ok" << std::endl;
	}
	// -----------  show result  --------------

	//std::cout<<"show"<<std::endl;
	//outPts.clear();

	std::cout << "generateStitches" << std::endl;
	for(std::vector<fillPatch>::size_type curPatch = 0; curPatch < patchs.size(); curPatch++)
	{
		if (!generateStitches(gridPts, patchs[curPatch]))
		{
			std::cout << "problem in generateStitches" << std::endl;
			return;
		}
		std::cout << "generateStitches ok" << std::endl;
	}

	std::cout << "prolongateSegments" << std::endl;
	if (!prolongateSegments(segments))
	{
		std::cout << "problem in prolongateSegments" << std::endl;
		return;
	}
	std::cout << "prolongateSegments ok" << std::endl;


	std::cout << "cutStitches" << std::endl;
	for(std::vector<fillPatch>::size_type curPatch = 0; curPatch < patchs.size(); curPatch++)
	{
		if (!cutStitches( segments, patchs[curPatch]))
		{
			std::cout << "problem in cutStitches" << std::endl;
			return;
		}
		std::cout << "cutStitches ok" << std::endl;
	}

	// fill outPts with every patch point
	for(std::vector<fillPatch>::size_type curPatch = 0; curPatch < patchs.size(); curPatch++)
	{
		fillPatch &patch = patchs[curPatch];
		outPts.insert(outPts.end(), patch.patchStitches.begin(), patch.patchStitches.end());
		patch.patchStitches.clear();
	}

	//clear pts and fill it with outPts (with eventually maxLen modifier)
	stitchPostProcess(outPts);

	needRefresh = false;
}

void OE_fillstitch::delDependency(OE_base * object)
{
	if (curve == object)
	{
		curve = nullptr;
	}
}

void OE_fillstitch::refreshDependency()
{
	if (curve)
	{
		curve->addDependency(this);
		curve->refreshDependency();
	}
}

