#include "engine.h"
#include "camera.h"

#include "logger.h"
extern rRenderer *g_Renderer;
extern rWindowing *w;
extern Logger g_Logger;
extern CCamera g_Camera;

extern long curFrame;
extern char mapName[256];
extern HANDLE g_EndFrameSignal;

char error[] = "Error in cfg file: '{' expected.";

engine::~engine()
{
	file.close();
}

void engine::MakeInterpolatorActive()
{
	if(!interpolators.empty())
	{
		activeInterpolators.push_back(interpolators.front());
		interpolators.pop_front();
	}
}

bool engine::Init(char filename[])
{
	file.open(filename, ifstream::in);

	if(!file.is_open())
	{
		g_Logger.Log("Could not load config file %s.", filename);
		return false;
	}
	
	char token[64];
	char token2[256];	//token for level 2 block
	file>>token;

	if(strcmp(token, "rcfg") != 0)
	{
		g_Logger.Log("File %s is not a valid config file.", filename);
		return false;
	}

	frameOfNextEvent = 0;
	rEvent *event;
	int num;
	while(file>>token, token[0])
	{
		if(strcmp(token, "}") == 0)
			continue;
		if(token[0] == ';')
		{
			file.getline(token2, 256);	//Ignore comments
			continue;
		}
		if(strcmp(token, "display") == 0)
		{
			event = new rInitDisp;
			event->type = disp;
			event->frame = -2; //execute immediately
		}else
		if(strcmp(token, "renderer") == 0)
		{
			event = new rInitRend;
			event->type = rend;
			event->frame = -1;
		}else
		if(strcmp(token, "map") == 0)
		{
			file>>token;
			if(strcmp(token, "{") != 0)
			{
				g_Logger.Log(error);
				return false;
			}
			file>>token;
			if(strcmp(token, "maps") == 0)
			{
				event = new rMapChange;
				event->type = mapc;
			}//add here other map events
		}else	
		if(strcmp(token, "camera") == 0)
		{
			file>>token;
			if(strcmp(token, "{") != 0)
			{
				g_Logger.Log(error);
				return false;
			}
			file>>token;
			if(strcmp(token, "waypoints") == 0)
			{
				event = new rCamWP;
				event->type = cwp;
			}
			if(strcmp(token, "calpha") == 0)
			{
				event = new rCamWP;
				event->type = ca;
			}
		}else
		if(strcmp(token, "text") == 0)
		{
			event = new rTextShow;
			event->type = txt;
		}else
		if(strcmp(token, "sound") == 0)
		{
			event = new rPlaySound;
			event->type = snd;
		}
		else
		if(strcmp(token, "caminteractive") == 0)
		{
			event = new rCamInteract;
			event->type = caminteract;
		}else
		if(strcmp(token, "motionblur") == 0)
		{
			event = new rMotionBlur;
			event->type = mb;
		}
		file>>token;	//Get inside block
		if(strcmp(token, "{") != 0)
		{
			g_Logger.Log(error);
			return false;
		}
		switch(event->type)
		{
		case disp:
			while(strcmp(token2, "}") != 0)
			{
				file>>token2;
				switch(token2[0])	//match by first letter only
				{
				case 'w':
					file>>token2;
					sscanf(token2, "%d", &num);
					event->intArgs[width] = num;
					break;
				case 'h':
					file>>token2;
					sscanf(token2, "%d", &num);
					event->intArgs[heigth] = num;
					break;
				case 'd':
					file>>token2;
					sscanf(token2, "%d", &num);
					event->intArgs[depth] = num;
					break;
				case 'f':
					file>>token2;
					sscanf(token2, "%d", &num);
					event->intArgs[full_screen] = num;
					break;
				}
			}
			events.push_back(event);
			break;
		case rend:
			while(file>>token2, strcmp(token2, "}") != 0)
			{	
				switch(token2[0])
				{
				case 'o':
					event->intArgs[0] = opengl;
					break;
				default:
					event->intArgs[0] = not_supported;
				}
			}
			events.push_back(event);
			break;
		case txt:
			while(file>>token2, strcmp(token2, "}") != 0)
			{	
				if(strcmp(token2, "frame") != 0)
				{
					g_Logger.Log("map identifier expected");
					continue;
				}
				file>>event->frame;
				file>>event->intArgs[0];
				file>>event->intArgs[1];
				file>>event->floatArgs[0];
				file>>event->floatArgs[1];
				file>>event->floatArgs[2];
				file>>event->intArgs[2];
				file.ignore(1);
				file.getline(event->charArgs,256);
				events.push_back(event);
				event = new rTextShow;
				event->type = txt;
			}
			delete event;
			break;
		case mapc:
			while(file>>token2, token2[0] != 0 && strcmp(token2, "}") != 0)
			{
				if(strcmp(token2, "map") != 0)
				{
					g_Logger.Log("map identifier expected");
					continue;
				}
				file>>event->charArgs;
				file>>token2;
				if(strcmp(token2, "frame") != 0)
				{
					g_Logger.Log("frame identifier expected");
					continue;
				}
				file>>event->frame;
				events.push_back(event);
				event = new rMapChange;
				event->type = mapc;
			}
			delete event;
			break;
		case cwp:
			int size;
			file>>token2;
			if(strcmp(token2, "num") != 0)
			{
				g_Logger.Log("num identifier expected");
				continue;
			}
			file>>size;

			rInterpolator *interp;
			interp = new rBezier;

			rInterpolator *interv;
			interv = new rBezier;

			vector3 *pos, *view;
			float *tf;

			pos = new vector3 [size];
			view = new vector3 [size];
			tf = new float [size];

			file>>token2;
			if(strcmp(token2, "frame") != 0)
			{
				g_Logger.Log("frame of first camera control expected");
				continue;
			}
			file>>event->frame;

			interp->setStartPoint((float)event->frame);
			interv->setStartPoint((float)event->frame);

			interp->attachVar(g_Camera.PositionRef());
			interv->attachVar(g_Camera.ViewRef());

			for(int i=0; i<size; i++)
			{
				file>>pos[i].x;
				file>>pos[i].y;
				file>>pos[i].z;

				file>>view[i].x;
				file>>view[i].y;
				file>>view[i].z;

				file>>tf[i];
			}
			interp->attachPath(pos, tf, size);
			interv->attachPath(view, tf, size);

			delete [] pos;
			delete [] view;
			delete [] tf;

			event->intArgs[0] = 2;
			events.push_back(event);
			interpolators.push_back(interp);
			interpolators.push_back(interv);
			break;
		case ca:
	
			rInterpolator *interc;
			interc = new rBezier;

			vector3 *col;
//			float *tf;

			col = new vector3 [3];
			tf = new float [3];

			file>>token2;
			if(strcmp(token2, "frame") != 0)
			{
				g_Logger.Log("frame of first camera control expected");
				continue;
			}
			file>>event->frame;

			interc->setStartPoint((float)event->frame);

			interc->attachVar(g_Camera.ColorRef());

			for(int i=0; i<3; i++)
			{
				file>>col[i].x;
				file>>col[i].y;
				file>>col[i].z;

				file>>tf[i];
			}
			interc->attachPath(col, tf, 3);

			delete [] col;

			event->intArgs[0] = 1;
			events.push_back(event);
			interpolators.push_back(interc);
			break;

		case snd:
			while(file>>token2, strcmp(token2, "}") != 0)
			{	
				if(strcmp(token2, "frame") != 0)
				{
					g_Logger.Log("start frame of sound expected");
					continue;
				}
				file>>event->frame;
				file>>event->type;
				file>>event->charArgs;
				events.push_back(event);
				event = new rPlaySound;
				event->type = snd;
			}
			delete event;
			break;
		case caminteract:
			while(file>>token2, strcmp(token2, "}") != 0)
			{	
				if(strcmp(token2, "frame") != 0)
				{
					g_Logger.Log("start frame of camera control expected");
					continue;
				}
				file>>event->frame;
				file>>event->intArgs[0];
				events.push_back(event);
				event = new rCamInteract;
				event->type = caminteract;
			}
			delete event;
			break;
		case mb:
			while(file>>token2, strcmp(token2, "}") != 0)
			{	
				if(strcmp(token2, "frame") != 0)
				{
					g_Logger.Log("start frame of camera control expected");
					continue;
				}
				file>>event->frame;
				file>>event->floatArgs[0];
				file>>event->floatArgs[1];
				events.push_back(event);
				event = new rMotionBlur;
				event->type = mb;
			}
			delete event;
			break;
		}
	}
	events.sort(greater<rEvent*>());
	interpolators.sort(greater<rInterpolator*>());

	return true;
}

void engine::Update()
{
	WaitForSingleObject(g_EndFrameSignal, 100);

	bool loop = true;
	while(loop)
	{
		if(events.empty())
			break;
		rEvent *e = events.front();
			
		if(e->frame <= curFrame)
		{
			e->execute();
			events.pop_front();
			delete e;
		}
		else
		{
			loop = false;
		}
	}

	WaitForSingleObject(g_RenderStateMutex, INFINITE);

	if(g_Renderer)
		g_Camera.Update();	

	if(!activeInterpolators.empty())
	{
		list<rInterpolator*>::iterator li = activeInterpolators.begin();
		while(li != activeInterpolators.end())
		{
			if(!(*li)->Update())
			{
				delete *li;
				if(li == activeInterpolators.end())
				{
					activeInterpolators.erase(li);
					break;
				}else
				{
					list<rInterpolator*>::iterator temp = li;
					li++;
                    activeInterpolators.erase(temp);
				}
			}
			else
				li++;
		}	
	}
	ReleaseMutex(g_RenderStateMutex);
}


//Event classes method definitions:

void rPlaySound::execute()
{
	BOOL r;
	if(type == 0)
		r = PlaySound(charArgs,NULL,SND_NODEFAULT|SND_FILENAME|SND_ASYNC|SND_LOOP);
	if(type == 1)
		r = PlaySound(charArgs,NULL,SND_NODEFAULT|SND_FILENAME|SND_ASYNC);
	if(!r)
	{
		g_Logger.Log("Sound file %s not found", charArgs);
	}
}

void rTextShow::execute()
{
	tx.x = intArgs[0];
	tx.y = intArgs[1];
	tx.endTime = intArgs[2] + curFrame;
	strcpy(tx.text, charArgs);
	tx.color.x = floatArgs[0];
	tx.color.y = floatArgs[1];
	tx.color.z = floatArgs[2];
	t->AttachText(tx);
}

void rMapChange::execute()
{
	strcpy(mapName, charArgs);

	if(g_Renderer->isRendering())
		g_Renderer->Suspend();
		
	g_Renderer->Resume();
};

void rInitRend::execute()
{
	if(intArgs[0] == opengl)
	{
		g_Renderer = new rOpenGL;
		g_Renderer->Create();
	}else
	{
		g_Logger.Log("Not supported renderer");
		exit(0);
	}
}

void rInitDisp::execute()
{
	w = new rWindowing;
	w->SetScreenParam("Rouslan Dimitrov 3D Reel for MPI Saarbruecken", intArgs[width],
		intArgs[heigth],
		intArgs[depth],
		intArgs[full_screen]? true : false);
	w->Create();
}

void rCamInteract::execute()
{
	g_Camera.SetInteractive((bool)intArgs[0]);
}

void rMotionBlur::execute()
{
	if(floatArgs[0] != 0 || floatArgs[1] != 0)
		g_Renderer->enableMotionBlur(floatArgs[0], floatArgs[1]);
	else
		g_Renderer->disableMotionBlur();
}