#pragma once

#include "..\DXFunctions\DXVector2.h"
#include "..\Utilities\list.h"
#include "..\Utilities\util.h"
#include "..\Utilities\Archive.h"

struct Profile
{
	float Min;
	float Max;
	float Var;
	LList<DXVector2> Points; //this list must always be sorted, for that use addpoint and dont do it on your own

    Profile(void)
	{
		New();
	}

	~Profile(void)
	{
		Points.Clear();
	}

	Profile(const Profile&p)
	{
		Min = p.Min;
		Max = p.Max;
		Var = p.Var;
		Points.CopyDataFrom(p.Points);
	}

	void operator =(const Profile&p)
	{
		Min = p.Min;
		Max = p.Max;
		Var = p.Var;
		if(&p != this) //if would copy to itself, points would be cleared
		{
			Points.Clear();
			Points.CopyDataFrom(p.Points);
		}
	}

	void New(void)
	{
		Min = Max = Var = 0;
		Points.Clear();
		Points.AddHead(new DXVector2(0.0f,1.0f),0);
		Points.AddTail(new DXVector2(1.f,1.0f),0);
	}

	int AddPoint(DXVector2 p)
	{
		int i = 0;
		ListElement<DXVector2> *pe = Points.GetHead();
		while(pe)
		{
			if(pe->GetInfo()->x > p.x)
			{
				Points.AddBefore(pe,new ListElement<DXVector2>(new DXVector2(p),0));
				return i;
			}
			pe = pe->GetNext();
			i++;
		}
		Points.AddTail(new DXVector2(p),0);
		return i;
	}

	float GetFactor(float s) const
	{
		if(Points.GetElementCount() > 1)
		{
			ListElement<DXVector2> *p0 = Points.GetHead();
			ListElement<DXVector2> *p1 =	p0->GetNext();
			
			while(p1->GetInfo()->x < s && p1->GetNext())
			{
				p0 = p1;
				p1 = p1->GetNext();
			}
			float st = (s - p0->GetInfo()->x)/(p1->GetInfo()->x - p0->GetInfo()->x);
			return Linear_Interpolate(p0->GetInfo()->y,p1->GetInfo()->y,st);
		}
		//if(Points.getElementCount() > 3)
		//{
		//	CElement<DXVector2> *p0 = Points.gethead()->GetNext();
		//	CElement<DXVector2> *p1 =	p0->GetNext();
		//	CElement<DXVector2> *p2 = p1->GetNext();
		//	CElement<DXVector2> *p3 = p2->GetNext();

		//	while(p2->getinfo()->x < s && p3->GetNext() != Points.gettail())
		//	{
		//		p0 = p1;
		//		p1 = p2;
		//		p2 = p3;
		//		p3 = p3->GetNext();
		//	}
 
		//	float st = (s - p1->getinfo()->x)/(p2->getinfo()->x - p1->getinfo()->x);
		//	if(st < 0.f)
		//		return p1->getinfo()->y;
		//	else if(st > 1.f)
		//		return p2->getinfo()->y;

		//	return Cubic_Interpolate(p0->getinfo()->y,p1->getinfo()->y,p2->getinfo()->y,p3->getinfo()->y,st);
		//}
		//else if(Points.getElementCount() > 2) //==3
		//{
		//	DXVector2 p1 = *(Points.gethead()->GetNext()->GetNext()->getinfo());
		//	
		//	float st;
		//	if(s > p1.x) 
		//	{
		//		DXVector2 p2 = *(Points.gethead()->GetNext()->GetNext()->GetNext()->getinfo());
		//		st = (s - p1.x)/(p2.x - p1.x);
		//		if(st > 1.f)
		//			return p2.y;
		//		return Cosine_Interpolate(p1.y,p2.y,st);
		//	}
		//	else
		//	{
		//		DXVector2 p0 = *(Points.gethead()->GetNext()->getinfo());
		//		st = (s - p0.x)/(p1.x - p0.x);
		//		if(st < 0.f)
		//			return p0.y;
		//		return Cosine_Interpolate(p0.y,p1.y,st);
		//	}
		//}
		//else if(Points.getElementCount() > 1) //==2
		//{
		// 	DXVector2 p0 = *(Points.gethead()->GetNext()->getinfo());
		//	DXVector2 p1 = *(Points.gethead()->GetNext()->GetNext()->getinfo());
		//	return Linear_Interpolate(p0.y,p1.y,(s - p0.x)/(p1.x - p0.x));
		//}
		return 0.f;
	}

	float GetValue(float s,float rnd) const
	{
		float r = Min + GetFactor(s) * (Max - Min) + Var * rnd;
		if(r > Max)
			return Max;
		else if(r < Min)
			return Min;
		else 
			return r;
	}

	void fwrite(FILE*f) const
	{
		::fwrite(&Min,sizeof(float),1,f);
		::fwrite(&Max,sizeof(float),1,f);
		::fwrite(&Var,sizeof(float),1,f);
		DWORD c = Points.GetElementCount();
		::fwrite(&c,sizeof(DWORD),1,f);
		DXVector2 p;
		for(ListElement<DXVector2> *pe =Points.GetHead();pe;pe = pe->GetNext())
			::fwrite(pe->GetInfo(),sizeof(DXVector2),1,f);
	}

	void Write(ArchiveFile* const f) const
	{
		f->Write(&Min, sizeof(float));
		f->Write(&Max, sizeof(float));
		f->Write(&Var, sizeof(float));
		DWORD c = Points.GetElementCount();
		f->Write(&c, sizeof(DWORD));
		DXVector2 p;
		for(ListElement<DXVector2> *pe =Points.GetHead();pe;pe = pe->GetNext())
			f->Write(pe->GetInfo(), sizeof(DXVector2));
	}

	void fread(FILE*f)
	{
		Points.Clear();
		::fread(&Min,sizeof(float),1,f);
		::fread(&Max,sizeof(float),1,f);
		::fread(&Var,sizeof(float),1,f);
		DWORD c;
		DXVector2 p;
		::fread(&c,sizeof(DWORD),1,f);
		for(c;c;c--)
		{
			::fread(&p,sizeof(DXVector2),1,f);
			Points.AddTail(new DXVector2(p),0);
		}
	}

	void Read(ArchiveFile* const f)
	{
		Points.Clear();
		f->Read(&Min, sizeof(float));
		f->Read(&Max, sizeof(float));
		f->Read(&Var, sizeof(float));
		DWORD c = 0;
		DXVector2 p;
		f->Read(&c, sizeof(DWORD));
		for(c;c;c--)
		{
			f->Read(&p, sizeof(DXVector2));
			Points.AddTail(new DXVector2(p),0);
		}
	}

	void operator *= (float f)
	{
		Min *= f;
		Max *= f;
		Var *= f;
	}
};