#ifndef __IESPARSER__H__
#define __IESPARSER__H__
#include <wx/filename.h>
#include "textparser.h"
#include "iomodule.h"

#define VERTICALMAX 360 ////hmmm 360
#define HORIZONTALMAX 360
namespace VRUT
{
	class IESLight
	{
	private:
		// struct of ies format
		int numberOfVertical;
		int numberOfHorizontal;
		std::vector<float> verticalAngles;
		std::vector<float> horizontalAngles;
		std::vector<float> intenzity;
		//following properties are read from file but not yet practically used
		bool meters;
		int nLamps; //number of lamps, default: 1
		float lumens; //lumens per lamp, default: - 1
		float multiplyingFactor; // candela multiplier, default: 1
		int PType; //1 == C phootometric type, 2 == B phootometric type, 3 == A phootometric type, 
		Vector3 wlh; //width, length, and height  //luminous  opening, default: 0 0 0
		float ballastFactor; // all candela value must with multiple ballast 
		float ballastLampPhotomFactor;
		float inputWats;// measured in wats;

	public:
		//constructor
		IESLight() {}
		IESLight(const wxString &fname)
		{
			Load(fname);
		}

		//destructor
		~IESLight() {}

		void Load(const wxString &fname)
		{
			verticalAngles.clear();
			horizontalAngles.clear();
			intenzity.clear();
			wxInputStream * is = IOModule::GetInputStream(fname);
			if (!is)
				return;
			TextParser textParser(is, 100000, 100);

			int tmpInt = 0;
			float tmpFloat= 0.0f;
			wxString tmp_string;
			//lost not interesting data
			while (!textParser.IsEmpty())
			{
				if (textParser.GetWxLine() == wxT("TILT=NONE"))
					break;
			}

			textParser.ParseInt(&nLamps, " \n\r\t", true); // not interested 1
			textParser.ParseFloat(&lumens, " \n\r\t");
			textParser.ParseFloat(&multiplyingFactor, " \n\r\t");

			textParser.ParseInt(&numberOfVertical, " \n\r\t", true);
			textParser.ParseInt(&numberOfHorizontal, " \n\r\t", true);
			textParser.ParseInt(&PType, " \n\r\t", true);
			textParser.ParseInt(&tmpInt, " \n\r\t", true); // feet(1) or meters(2)
			if (tmpInt == 1)
				meters = false;
			textParser.ParseVector(wlh._v, 3, 0, " \n\r\t", true);
			textParser.ParseFloat(&ballastFactor, " \n\r\t"); // not interested 1
			textParser.ParseFloat(&ballastLampPhotomFactor, " \n\r\t"); // not interested 1 
			textParser.ParseFloat(&inputWats, " \n\r\t"); // not interested 0

			verticalAngles.reserve(numberOfVertical);
			for (int i = 0; i < numberOfVertical; i++)
			{
				textParser.ParseFloat(&tmpFloat, " \n\r\t");
				verticalAngles.push_back(tmpFloat);
			}

			horizontalAngles.reserve(numberOfHorizontal);
			for (int i = 0; i < numberOfHorizontal; i++)
			{
				textParser.ParseFloat(&tmpFloat, " \n\r\t");
				horizontalAngles.push_back(tmpFloat);
			}
			//size of ies
			int size = numberOfHorizontal * numberOfVertical;
			intenzity.reserve(size);
			float max = 0.0f;
			//read intensities and find maximal value
			for (int i = 0; i < numberOfHorizontal; i++)
			{
				for (int j = 0; j < numberOfVertical; j++)
				{
					textParser.ParseFloat(&tmpFloat, " \n\r\t");
					intenzity.push_back(tmpFloat);
					max = std::max<float>(max, tmpFloat);
				}
			}
			//normalize
			/*
			for(int i = 0;i < size; i++)
				intenzity[i] /= max;*/
		}

		float GetIntensity(float ha, float va)
		{
			float tmpReturn = 0.0f;

			int hf = 0;//horizontal first
			int hl = 0; // horizontal last
			if (horizontalAngles.empty() || verticalAngles.empty())
				return 0.0f;
			if (ha < horizontalAngles[0]) //miss
				return 0.0f;
			else if (ha > horizontalAngles[numberOfHorizontal -1]) //miss
				return 0.0f;
			else
			{
				float halfI = 0.0;

				halfI = numberOfHorizontal - 1;
				halfI /= 2;
				int i = halfI;
				while(true)
				{
					if (ha == horizontalAngles[i])
					{
						break;
					}
					else if ((ha <= horizontalAngles[i]) && (ha >= horizontalAngles[i - 1]))
					{
						break;
					}
					else if (ha < horizontalAngles[i])
					{
						if (halfI > 2)
							halfI /= 2;
						i -=halfI;
					}
					else if (ha > horizontalAngles[i])
					{
						if (halfI > 2)
							halfI /= 2;
						i += halfI;
					}
				}

				hl = i;
				if (i == 0)
				{
					hl = 1;
					hf = 0;
				}
				else
					hf = i - 1;
			}

			int vf = 0; // vertical first
			int vl = 0; // vertical last
			if (va < verticalAngles[0])	//miss
				return 0.0f;
			else if (va > verticalAngles[numberOfVertical - 1]) //miss
				return 0.0f;
			else
			{
				float halfI = 0.0;
				halfI = numberOfVertical - 1;
				halfI /= 2;
				int i = halfI;
				while (true)
				{
					if (va == verticalAngles[i])
					{
						break;
					}
					else if ((va <= verticalAngles[i]) && (va >= verticalAngles[i - 1]))
					{
						break;
					}
					else if (va < verticalAngles[i])
					{
						if (halfI > 2)
							halfI /= 2;
						i -=halfI;
					}
					else if (va > verticalAngles[i])
					{
						if (halfI > 2)
							halfI /= 2;
						i +=halfI;
					}
				}

				vl = i;
				if (vl == 0)
				{
					vl = 1;
					vf = 0;
				}
				else
					vf = i - 1;
			}

			//merge point
			float Q11 = 0.0;
			float Q12 = 0.0;
			float Q21 = 0.0;
			float Q22 = 0.0;

			Q11 = intenzity[(hf * numberOfVertical) + vf];
			Q22 = intenzity[(hl * numberOfVertical) + vl];
			Q21 = intenzity[(hf * numberOfVertical) + vl];
			Q12 = intenzity[(hl * numberOfVertical) + vf];

			float a = (va - verticalAngles[vf]) / (verticalAngles[vl] - verticalAngles[vf]);
			float b = (ha - horizontalAngles[hf]) / (horizontalAngles[hl] - horizontalAngles[hf]);
			tmpReturn = a * ( b * Q22 + (1 - b) * Q21) + (1 - a) * ( b * Q12 + ( 1 - b) * Q11);

			return tmpReturn;
		}

		std::vector<float> get2Dtex(int x, int y)
		{
			size_t size = x * y;
			float horA = 0.0f;
			float verA = 0.0f;
			float horS = HORIZONTALMAX / (float)x;
			float verS = VERTICALMAX / (float)y;
			std::vector<float> returnValue((size), 0.0f);

			size_t  index = 0;
			
			//distance2 = std::exp((distance2/(scale)) + 1.0f);
			//distance2 = std::log10f((distance2) + 1.0f);	

			//float max = 0.0f; //for debug
			for(int j = 0; j < y; j++)
			{
				for(int i = 0; i < x; i++)
				{
					index = j * x + i;
					horA = (i * horS) - 180;
					verA = (j * verS) - 180; //-90
					returnValue[index] = GetIntensity(horA, verA);
					//max = std::max<float>( max ,returnValue[index] );
				}
			}

			/*
			for(size_t i= 0; i < size ;i++)
				returnValue[i]  /= max;
				*/
			return returnValue;
		}
		std::vector<float> get3Dtex(int x, int y, int z, float scale)
		{
			//float scaleMulti = 0.18181818182f;
			float scaleMultiX = scale / x;//1;//0.18181818f;
			float scaleMultiY = scale / y;//1;//0.18181818f;
			float scaleMultiZ = scale / z;//1;//0.18181818f;
			//scale = 1.0f;
			int midleX = (x / 2) * scaleMultiX;
			int midleY = (y / 2) * scaleMultiY;
			//int midleX = (x / 2) ;
			//int midleY = (y / 2) ;


			Vector3 center (midleX, midleY, 0);
			Vector3 point (midleX, midleY, z * scaleMultiZ);
			//Vector3 point (midleX, midleY, z );
			Vector3 vector = point - center; // vector with 0degrese horizontal and 0 degrase vertical
			vector = vector.Normalize();
			int size = x * y * z;
			std::vector<float> tmpReturn(size,0.0f);
			float mx = midleX;// + 0.0001;
			float my = midleY;// + 0.0001;
			//tmpReturn.reserve();
			int tmp_index = 0;
			float distance2 = 0.0;
			Vector3 tmpVector;
			float verticalA = 0.0f;
			float horizontalA = 0.0f;
			float tmpIntezity = 0.0f;
			float max = 0.0f;
			//float min = FLT_MAX;
			Vector3 testV = Vector3(0.0, 0.0, 0.0);
			for ( int k = 1; k < z - 1; k++)
			{
				for (int j = 0; j < y; j++)
				{
					for ( int i = 0; i < x; i++)
					{
						//scaleMulti = log10f( k * j * i * 0.05f );
						/*
						if(  i ==101 && j > 99 && k > 15)
						printf("");*/
						tmp_index = (k * x * y) + (j * x) + i; 
						//point = Vector3( i, midleY, k);
						point = Vector3( mx, j * scaleMultiY, k * scaleMultiZ);
						//point = Vector3( mx, j /** scaleMulti*/, k /** scaleMulti*/);

						tmpVector = point - center;
						//tmpVector = tmpVector.Normalize();
						if(tmpVector == testV)
							verticalA = 0.0f;
						else if(j * scaleMultiY  > midleY)
							verticalA = - RAD2DEG*acosf(vector.Dot(tmpVector.Normalize()));
						else
							verticalA = RAD2DEG*acosf(vector.Dot(tmpVector.Normalize()));
						//point = Vector3( midleX, j, k);
						point = Vector3( i * scaleMultiX, my, k * scaleMultiZ);
						//point = Vector3( i /** scaleMulti*/, my, k /** scaleMulti*/);
						tmpVector = point - center;
						//tmpVector = tmpVector.Normalize();
						if(tmpVector == testV)
							horizontalA = 0.0f;
						else if(i * scaleMultiX > midleX)
							horizontalA = -RAD2DEG*acosf(vector.Dot(tmpVector.Normalize()));
						else
							horizontalA = RAD2DEG*acosf(vector.Dot(tmpVector.Normalize()));

						tmpIntezity = GetIntensity(horizontalA, verticalA);

						//distance2 = ( (i - center.x) * (i - center.x) + (j - center.y) * (j - center.y) + (k - center.z) * (k - center.z));
						distance2 = (i * scaleMultiX - center.x) * (i * scaleMultiX - center.x) + (j * scaleMultiY - center.y) * (j * scaleMultiY - center.y) + (k * scaleMultiZ  - center.z) * (k * scaleMultiZ - center.z);// neeed r^2
						//distance2 = std::sqrt((i * scaleMulti - center.x) * (i * scaleMulti - center.x) + (j * scaleMulti - center.y) * (j * scaleMulti - center.y) + (k * scaleMulti  - center.z) * (k * scaleMulti - center.z));// neeed r^2

						//distance2 = std::log(std::sqrt ( (i - center.x) * (i - center.x) + (j - center.y) * (j - center.y) + (k - center.z) * (k - center.z)));
						//distance2 = distance2*distance2;

						if(distance2 == 0.0)
							distance2 = 0.00001f;
						/*
						else
						minD =  std::min<float>(minD, distance2);
						*/
						//tmpReturn[tmp_index] = std::log10f(tmpIntezity / std::exp((distance2/(scale)) + 1.0f));
						//tmpReturn[tmp_index] = std::log10f(tmpIntezity / std::log10f((distance2) + 1.0f));

						//distance2 = std::exp((distance2/(scale)) + 1.0f);
						//distance2 = std::log10f((distance2) + 1.0f);				
						//tmpReturn[tmp_index] = std::log10f(tmpIntezity / (distance2 * distance2));


						//tmpReturn[tmp_index] = log10f(tmpIntezity / (distance2));
						tmpReturn[tmp_index] = tmpIntezity / distance2;
						//tmpReturn.push_back(tmpIntezity / distance2);
						/*
						if(max < tmpReturn[tmp_index])
						{
						float tmpfff = tmpReturn[tmp_index];
						printf("");
						}*/
						max = std::max<float>(max, tmpReturn[tmp_index]);
						/*
						min = std::min<float>(min, tmpReturn[tmp_index]);
						if(tmpReturn[tmp_index]<0.0)
						{
						tmpReturn[tmp_index] = 0.0;
						printf("");
						}
						*/
					}
				}
			}

			//minD;
			//with cos alfa
			//max /=45.4;
			//without cosalfa

// 			max /= 15.0;
// 			//max *= 2.0;
// 			for(int i = 0;i < size; i++)
// 			{
// 				tmpReturn[i] /= max;
// 				//tmpReturn[i] = 1.0f;
// 				/*if(tmpReturn[i] <= 0.0000001)
// 				tmpReturn[i] = 0.0000001;*/
// 			}
			/*
			for(int i = 0;i < size; i++)
			{
			if(tmpReturn[tmp_index]> 1.0)
			printf("");
			}
			*/
			return tmpReturn;

		}		
		std::vector<float> GetVerticalAngles()
		{
			return verticalAngles;
		}
		std::vector<float> GetHorizontalAngles()
		{
			return horizontalAngles;
		}
		std::vector<float> GetIntenzityData()
		{
			return intenzity;
		}
	};
}

#endif
