#pragma once
#include "config.h"
#include "includes.h"

typedef DT_FP type_float;

inline double square(double a) {
	return a*a;
}

template <typename _Ty> class RandomQueue {
	private:
		vector<_Ty>* _data;
	public:
		RandomQueue() {
			_data = new vector<_Ty>();
		}
		~RandomQueue() {
			delete _data;
		}
		inline bool empty(void) {
			return _data->empty();
		}
		_Ty pop(void) {
			int index = rand()%_data->size();
			_Ty object = _data->at(index);
			_data->erase(_data->begin()+index);
			return object;
		}
		void push(_Ty obj) {
			_data->push_back(obj);
		}
};

class PoissonPoint {
	public:
		type_float x,y;
		PoissonPoint(type_float x, type_float y) {
			this->x = x;
			this->y = y;
		}
};
inline bool compare_points_box(PoissonPoint* a, PoissonPoint* b) {
	/*return (
		a->x < b->x
		&&
		a->y < b->y
	) ? true : false;*/
	return max(a->x,a->y) < max(b->x,b->y);
}
inline bool compare_points_circle(PoissonPoint* a, PoissonPoint* b) {
	return square(a->x-0.5)+square(a->y-0.5) < square(b->x-0.5)+square(b->y-0.5); //comparse r^2
}

inline bool in_domain_box(PoissonPoint* p) {
	return (p->x>0.0&&p->y>0.0&&p->x<1.0&&p->y<1.0) ? true : false;
}
inline bool in_domain_circle(PoissonPoint* p) {
	return (square(p->x-0.5)+square(p->y-0.5)<0.25) ? true : false; //comparse r^2
}

void imageToGrid(PoissonPoint* point, int grid_dim, int* x, int* y);
bool inNeighborhood(PoissonPoint*** grid, PoissonPoint* point, type_float mindist, type_float cell_size, int grid_dim);
PoissonPoint* generateRandomPointAround(PoissonPoint* point, type_float mindist);

list<PoissonPoint*>* generate_poisson(type_float min_dist, int new_points_count, bool(*in_domain)(PoissonPoint*) );

list<PoissonPoint*>* generate_poisson_helper(
	double r,
	unsigned int num_points,
	int trial_points,
	bool(*comparator)(PoissonPoint*,PoissonPoint*),
	bool(*in_domain)(PoissonPoint*)
);
inline list<PoissonPoint*>* generate_poisson_square(unsigned int num_points, int trial_points=30) {
	//Maximum density of grid should be 2/(r*r*sqrt(3)), where r is the distance between points (triangular grid)
	//Solve num_points=density*area for r

	//This should give us an estimate of the radius required to get the right number of sample points back.
	//In practice, we'll need more, because Poisson disk is not a triangular grid.  This fudge factor will
	//be added reciprocally.

	static type_float sqrt2_over_fourth3 = pow(2.0,0.5) / pow(3.0,0.25);

	type_float r = sqrt2_over_fourth3 / ((type_float)(1.25)*pow( (type_float)(num_points), (type_float)(0.51) ));

	return generate_poisson_helper(r,num_points,trial_points,&compare_points_box,&in_domain_box);
}
inline list<PoissonPoint*>* generate_poisson_disk(unsigned int num_points, int trial_points=30) {
	//Maximum density of grid should be 2/(r*r*sqrt(3)), where r is the distance between points (triangular grid)
	//Solve num_points=density*area for r

	//This should give us an estimate of the radius required to get the right number of sample points back.
	//In practice, we'll need more, because Poisson disk is not a triangular grid.  This fudge factor will
	//be added reciprocally.

	static type_float sqrt2_over_fourth3 = pow(2.0,0.5) / pow(3.0,0.25);
	static type_float circle_factor = 1.0 / (0.25*glLib::Math::Misc::pi);

	type_float r = sqrt2_over_fourth3 / ((type_float)(1.3)*pow( (type_float)(num_points)*circle_factor, (type_float)(0.51) ));

	return generate_poisson_helper(r,num_points,trial_points,&compare_points_circle,&in_domain_circle);
}