#ifndef PARAMS_H
#define PARAMS_H

#include <cmath>
#include <iostream>
#include <string.h>

#define PI 3.14159265f

///helper class for the PolarListAgent agents
class PolarPair {
 public:
	PolarPair() { ang = 0.f ; dist = 0.f; }
   PolarPair(const float a, const float d) :
		ang(a), dist(d) {}

	float ang,dist;
};


///helper class to sort lists of this kind
class SortPolarPair {
 public:
	bool operator()(const PolarPair& a, const PolarPair& b) const
	{ return(a.ang < b.ang); }
};


///simply a 3D vector...
template <typename T>
class Vector3d {
 public:
	///the vector data
  	T x,y,z;

	///default constructor...
	Vector3d(void) :
		x(0), y(0), z(0) {}

	///init constructor...
	Vector3d(const T xx,const T yy,const T zz) :
		x(xx), y(yy), z(zz) {}

	///init constructor...
	Vector3d(const T xyz) :
		x(xyz), y(xyz), z(xyz) {}

	///copy constructor...
	Vector3d(const Vector3d<T>& vec)
	{
		this->x=vec.x;
		this->y=vec.y;
		this->z=vec.z;
	}

	void Reset(void)
	{ x=y=z=0; }

	T Len() const { return (T)sqrt (x*x + y*y + z*z); }
	T Mod() const{ return (T)sqrt (x*x + y*y + z*z); }
	T Mod2() const{ return x*x + y*y + z*z; }
	T LenQ()const { return x*x + y*y + z*z; }
	T Sum() { return x + y + z; }
	Vector3d<T> Ort() const { T l = Len(); return l!=0.f ? Vector3d<T>(x/l, y/l, z/l) : Vector3d<T>(0, 0, 0); }

	Vector3d<T>& operator=(const Vector3d<T>& vec)
	{
		this->x=vec.x;
		this->y=vec.y;
		this->z=vec.z;
		return( *this );
	}

	/// unary minus = negate
	Vector3d<T> operator-() const { return Vector3d<T>(-x,-y,-z); }

	Vector3d<T>& operator=(const T scalar)
	{
		this->x=scalar;
		this->y=scalar;
		this->z=scalar;
		return( *this );
	}

	Vector3d<T>& operator+=(const Vector3d<T>& vec)
	{
		this->x+=vec.x;
		this->y+=vec.y;
		this->z+=vec.z;
		return( *this );
	}

	Vector3d<T>& operator-=(const Vector3d<T>& vec)
	{
		this->x-=vec.x;
		this->y-=vec.y;
		this->z-=vec.z;
		return( *this );
	}

	Vector3d<T>& operator*=(const T& scal)
	{
		this->x*=scal;
		this->y*=scal;
		this->z*=scal;
		return( *this );
	}

	Vector3d<T>& operator/=(const T& scal)
	{
		this->x/=scal;
		this->y/=scal;
		this->z/=scal;
		return( *this );
	}
	T Angle(const Vector3d<T> &p2) const
	{
		return (T)(acos(Ort() * p2.Ort()) * 180.0 / PI);
	}
};

template <class T> inline Vector3d<T> operator+ (const Vector3d<T>& v1,
	const Vector3d<T>& v2) {
		Vector3d<T> v = v1; v += v2; return v; }
template <class T> inline Vector3d<T> operator- (const Vector3d<T>& v1,
	const Vector3d<T>& v2) {
		Vector3d<T> v = v1; v -= v2; return v; }
template <class T> inline T operator * (const Vector3d<T> &p,
	const Vector3d<T> &p1) {
		return p.x * p1.x + p.y * p1.y + p.z * p1.z; } 
template <class T> inline Vector3d<T> operator+ (const Vector3d<T>& v, T s) {
	return Vector3d<T>(v.x + s, v.y + s, v.z + s); }
template <class T> inline Vector3d<T> operator- (const Vector3d<T>& v, T s) {
	return Vector3d<T>(v.x - s, v.y - s, v.z - s); }
template <class T> inline Vector3d<T> operator* (const Vector3d<T>& v, T s) {
	return Vector3d<T>(v.x * s, v.y * s, v.z * s); }
template <class T> inline Vector3d<T> operator/ (const Vector3d<T>& v, T s) {
	return Vector3d<T>(v.x / s, v.y / s, v.z / s); }
template <class T> inline Vector3d<T> operator+ (T s, const Vector3d<T>& v) {
	return Vector3d<T>(s + v.x, s + v.y, s + v.z); }
template <class T> inline Vector3d<T> operator- (T s, const Vector3d<T>& v) {
	return Vector3d<T>(s - v.x, s - v.y, s - v.z); }
template <class T> inline Vector3d<T> operator* (T s, const Vector3d<T>& v) {
	return Vector3d<T>(s * v.x, s * v.y, s * v.z); }
template <class T> inline Vector3d<T> operator/ (T s, const Vector3d<T>& v) {
	return Vector3d<T>(s / v.x, s / v.y, s / v.z); }

template <class T>
inline Vector3d<T> Mul (const Vector3d<T> &u, const Vector3d<T> &v) 
{ 
	return Vector3d<T> (u.y * v.z - u.z * v.y, u.z * v.x - u.x * v.z, u.x * v.y - u.y * v.x); 
}
template <class T>
inline void Mul (const Vector3d<T> &u, const Vector3d<T> &v, Vector3d<T> &res) 
{ 
	res.x=u.y * v.z - u.z * v.y;
	res.y=u.z * v.x - u.x * v.z;
	res.z=u.x * v.y - u.y * v.x; 
}

typedef Vector3d<float> Vector3F;
typedef Vector3d<float> Vector3FC;

//shape of the vectors:
#define CONE_HALFANG	0.5236f		//30 deg
#define CONE_LENGTH 0.15f
//time-saver: params for the rotation matrix
const float cs=cosf(CONE_HALFANG);
const float sn=sinf(CONE_HALFANG);


///returns a-b in radians, function takes care of 2PI periodicity
float SignedAngularDifference(const float& a, const float& b);

///returns |a-b| in radians, function takes care of 2PI periodicity
float AngularDifference(const float& a, const float& b);



//finally, a class that contains all global parameters of the simulation
class ParamsClass {
 public:
 	//the scene, the playground for agents to stay within [um]
	Vector3d<float> sceneOffset;
	Vector3d<float> sceneSize;

	//helper centre...
	Vector3d<float> sceneCentre;

	//the outer width between the frame and window border [um]
	Vector3d<float> sceneOuterBorder;

	//colour of the frame around the playground rectangle
	struct {
		float r,g,b;
	} sceneBorderColour;

	//returns true if given position is inside the scene
	bool Include(const Vector3d<float> pos) const
	{
		return (	(pos.x >= sceneOffset.x)
				&& (pos.y >= sceneOffset.y)
				&& (pos.z >= sceneOffset.z)
				&& (pos.x < sceneOffset.x + sceneSize.x)
				&& (pos.y < sceneOffset.y + sceneSize.y)
				&& (pos.z < sceneOffset.z + sceneSize.z) );
	}


	//simulation params
	int numberOfAgents;

	float friendshipDuration;  //[min]
	float maxCellSpeed;        //[um/min]

	float cellCycleLength;      //[min]
	float cellPhaseDuration[8]; //[min]

	/**
	 * Initially, it is initial time of the simulation [min].
	 * But it is incremented as the simulation advances.
	 */
	float currTime;

	//how much to increment the \e currTime [min]
	float incrTime;

	//final time of the simulation [min]
	float stopTime;

	size_t imgSizeX, imgSizeY; //[pixels]
	float imgResX,imgResY;     //[pixels per micrometer]
	//all strings should include "%05d" substring to
	//indicate where file index number should be printed
	std::string imgOutlineFilename;
	std::string imgPhantomFilename,imgMaskFilename;
	std::string imgFluoFilename,imgPhCFilename;

	//a filename for the "tracks.txt" file
	std::string tracksFilename;

	//all strings should include "%d" substring (or some similar)
	//to allow for filenames indexing
	std::string inputCellsFilename;

};


/**
 * A wrapper for two or three images that should represent 2D or 3D vector
 * flow field.
 *
 * All images must be of the same size, resolution, and offset.
 * In the 2D case, the third \e z image is not be present, i.e. z==NULL.
 */
#include <i3d/image3d.h>
template <class FT>
struct FlowField {
  public:
	///empty constructor
  	FlowField() : x(NULL), y(NULL), z(NULL) {}

	///copy constructor: makes extra copies of flow fields
	FlowField(FlowField<FT> const &FF) {
		if (FF.x) x=new i3d::Image3d<FT>(*FF.x); else x=NULL;
		if (FF.y) y=new i3d::Image3d<FT>(*FF.y); else y=NULL;
		if (FF.z) z=new i3d::Image3d<FT>(*FF.z); else z=NULL;
	}

	///destructor
	~FlowField() { if (x) delete x; if (y) delete y; if (z) delete z; }

	///the vector elements
	i3d::Image3d<FT> *x;
	i3d::Image3d<FT> *y;
	i3d::Image3d<FT> *z;

	/**
	 * Is the flow field consistent? That is, \e x and \e y must exist
	 * and must be of the same size. If \e z exists as well, it must be
	 * also of the same size. If then \e x is 3D image, \e z must exists.
	 * Also, the resolution and offset of all images must be the same.
	 *
	 * \returns True, if consistent.
	 */
	bool isConsistent(void) const {
		if (!x) return false;
		if (!y) return false;
		if (x->GetSize() != y->GetSize()) return false;
		if (x->GetResolution().GetRes() != y->GetResolution().GetRes()) return false;
		if (x->GetOffset() != y->GetOffset()) return false;

		if ( (x->GetSizeZ() > 1) && (!z) ) return false;
		if (z) {
			if (x->GetSize() != z->GetSize()) return false;
			if (x->GetResolution().GetRes() != z->GetResolution().GetRes()) return false;
			if (x->GetOffset() != z->GetOffset()) return false;
		}
		return true;
	}
};


/// helper macro to unify reports:
#define REPORT(x) std::cout << __FUNCTION__ << "(): " << x << std::endl;
#define REPORT_NOENDL(x) std::cout << __FUNCTION__ << "(): " << x;

#ifdef SURFACE_DEBUG
	#define DEBUG_REPORT(x) std::cout << x << std::endl;
	#define DEBUG_REPORT_NOENDL(x) std::cout << x;
#else
	#define DEBUG_REPORT(x) 
	#define DEBUG_REPORT_NOENDL(x)
#endif

#endif
