//#  eulerN -- program for statistical estimation of Euler characteristic of a 3-dimensional body
//#  given as a sufficiently dense "volumetric" point sampling
//#
//#  Copyright (c) 2005-2007 Bradford Jones, Konstantin Rybnikov, Karen Daniels, Daniel Klain
//#  Release 2: 1 June 2007 
//#
//#  This program is free software; you can redistribute it and/or modify it
//#  under the terms of the GNU General Public License as published by the
//#  Free Software Foundation; either version 2, or (at your option) any
//#  later version: http://www.gnu.org/licenses/gpl.txt.
//#
//#  This program is distributed in the hope that it will be useful,
//#  but WITHOUT ANY WARRANTY; without even the implied warranty of
//#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//#  GNU General Public License for more details.
//#-----------------------------------------------------------------------------


#include "EulerSweep.h"
bool EulerSweep::hard;
double EulerSweep::smallT;
double EulerSweep::bigT;

//for debug
void printIntervals (stack<Interval> a)
{


	int max = a.size();
	for (int i = 0; i < max; i++)
	{
		cout << "		" << a.top() << endl;
		a.pop();
	}
}
bool EulerSweep::overlapInterval(Interval &a, Interval &b, double smallT, double bigT)
{
	//cout << "enter overlapInterval " << endl;
	double lengthOfIntersection = (a.intersection(b)).getLength();
	double minLength = min(a.getLength(), b.getLength());

	if (b.getUpper() <= a.getLower() || a.getUpper() <= b.getLower())
	{

		return false;
	}
	else if (lengthOfIntersection < smallT) // overlap is tiny on absolute scale
	{
		//cout << "enter if" << endl;
		if ( minLength > bigT) // intervals are big as compared to bigT
		{
		//	cout << "min lemgth is = " << minLength << endl;
			return false;
		}
		else if ( (lengthOfIntersection/minLength) <= (smallT/bigT) ) // overlap is small
		{
			return false;
		}
		else
			return true;
	}
	else
		return true;
}

// stacks should be ordered such that the lowest interval is at the top
int EulerSweep::matching(stack<Interval> SL, stack<Interval> SR, double smallT, double bigT)
{
	//cout << "	** calling matching " << endl;
	//cout << "      printing SL: "<< endl;
	//printIntervals(SL);
	//cout << "      printing SR: " << endl;
	//printIntervals(SR);

	int g = 0;
	Interval lastRight; ///empty initialization
	bool isLastRightEmpty = true;
	double l = INT_MIN;

	if (SR.size() != 0)
	{
		while (SL.size() != 0)
		{
			Interval I = SL.top();
			SL.pop();
			if (isLastRightEmpty == false)
			{

				if (overlapInterval(I, lastRight, smallT, bigT))
				{
					//cout << I << " and " << lastRight << " overlap " << endl;
					g = g -1;
				}
				if ( lastRight.getUpper() < I.getUpper() ) // current lastright cannot overlap with next interval on the left
				{
					isLastRightEmpty = true;
				}
			}
			while (l < I.getUpper()) // while the lower end of next interval on the right is below the upper end of I
			{

				Interval J = SR.top(); // J is the lowest unprocessed interval on the right
				SR.pop();

				if ( overlapInterval(I,J,smallT,bigT) == false) // a new branch is born onn the right
				{
				//	cout << I << " and " << J  << " do not overlap " << endl;

					g++;
				}
				else if (I.getUpper() < J.getUpper())
				{
					lastRight = J;
					isLastRightEmpty = false;
				}
				///////
				if ( SR.size() != 0)
				{
					l = SR.top().getLower();
				}
				else
				{
					l = INT_MAX;
				}
			}
		}
	}
	while (SR.size() != 0)
	{
		SR.pop();
		g++;
	}
	return g;
}

// constructs a stack of intervals where the lowest intervals are at the top of the stack
stack<Interval> EulerSweep:: constructIntervals (vector<double> &pointcloud, double stripwidth)
{
	stack<Interval> theIntervals;
	if (pointcloud.size() < 1) // return empty stack if there are no points
		return theIntervals;

	//sort data
	sort(pointcloud.begin(), pointcloud.end());

	double solidbegin = pointcloud.back();
	for (int i = pointcloud.size() -2 ; i >= 0 ; i--)
	{
		if ( (pointcloud[i+1] - pointcloud[i]) >= stripwidth)
		{
			if ( (solidbegin - pointcloud[i+1]) >= stripwidth )
			{
				theIntervals.push(Interval(solidbegin, pointcloud[i + 1])); //found a solid
			}

			solidbegin = pointcloud[i];
		}
	}
	if (solidbegin - pointcloud[0] >= stripwidth)
	{
		theIntervals.push(Interval(pointcloud[0], solidbegin)); //found a solid
	}

	return theIntervals;
}



int EulerSweep::euler2d (vector<d2point> &pointcloud, vector<double> &deltas, int numdirections, vector<math_vector> &directions)
{

	double delta = deltas[1];
	// CALLING
	//cout << "Calling sweep " << endl;
	if (pointcloud.size() == 0)
		return 0;

	int sumOfEuler = 0;
	int directionCounter = numdirections;
	bool isRandom = true;
	math_vector direction(0,0);

	if (directions.size() > 0)
	{
		isRandom = false;
		directionCounter = directions.size();
	}

	for (int i = 0; i < directionCounter; i++)
	{
		//generate ith direction
		if (isRandom)
		{
			direction = direction.getRandomUnitVector(2);
			if (direction[1] < 0) // we only want vectors pointing upwards
				direction = direction*-1; //flip directions
		}
		else
		{
			direction = directions[i];
			direction = direction.norm();
		}

		//cout << "using 2d direction = " << direction << endl;
		//find min along direction
		double min = (direction[0] * pointcloud[0].xcoord()) + (direction[1] * pointcloud[0].ycoord()); // dot product
		for (int i = 0; i < pointcloud.size(); i++)
		{
			double a = (direction[0] * pointcloud[i].xcoord()) + (direction[1] * pointcloud[i].ycoord());
			if (a < min)
				min = a;
		}
		//find max along direction
		double max = (direction[0] * pointcloud[0].xcoord()) + (direction[1] * pointcloud[0].ycoord()); // dot product
		for (int i = 0; i < pointcloud.size(); i++)
		{
			double a = (direction[0] * pointcloud[i].xcoord()) + (direction[1] * pointcloud[i].ycoord());
			if (a > max)
				max = a;
		}

		//sum
		int intervalsum = 0;
		double intervalStart = min;
		double intervalEnd = min + delta;
		vector<double> currentInterval;
		math_vector intervalDirection(2);

		stack<Interval> previousStrip;
		stack<Interval> currentStrip;


		while (intervalStart < max)
		{
			//cout << " Interval: " << intervalStart << " -> " << intervalEnd << endl;
			//find direction of current strip
			intervalDirection[0] = direction[1];
			intervalDirection[1] = -1 * direction[0];
			//cout << "Interval direction = " << intervalDirection << endl;

			//find points in current interval, add to currentInterval
			for (int i = 0; i < pointcloud.size(); i++)
			{
				double a = (direction[0] * pointcloud[i].xcoord()) + (direction[1] * pointcloud[i].ycoord());
				//if (a >= intervalStart && a <= intervalEnd)
				if (a >= intervalStart && a < intervalEnd) // notice >=, <
					currentInterval.push_back(intervalDirection[0]*pointcloud[i].xcoord() + intervalDirection[1]*pointcloud[i].ycoord());
			}
			// convert current interval into a stack of solid intervals, the stack size is also the E.C. !
			// E.C. in 1d is # of conected intervals

			currentStrip = EulerSweep::constructIntervals(currentInterval, deltas[0]);

			if (hard == true || currentStrip.size() != previousStrip.size() )
			{
				int value = matching(previousStrip, currentStrip, smallT, bigT);
					//cout << "   got matching() value = " << value << endl;
				//cout << "previousStrip.size() = " << previousStrip.size() << endl;
				intervalsum = intervalsum + value;
			}

			// prepare for next iteration

			previousStrip = currentStrip;
			currentInterval.clear();
			intervalStart = intervalEnd;
			intervalEnd = intervalStart + delta;
		}
		sumOfEuler = sumOfEuler + intervalsum;
	}

	double result =  (double) sumOfEuler/directionCounter;
	if ((ceil(result) - result) <= .5)
		return ((int)ceil(result));
	else
	return ((int)floor(result));
}
