//---------------------------------------------------------------------------
#include <string.h>
#include <limits.h>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#pragma hdrstop

#ifdef _WIN32
# include "SWI-cpp_hack.h"
#else
# include "SWI-cpp.h"
#endif

#include "Error.h"
#include "ImpExp.h"
#include "FSTools.h"
#include "PrologSources.h"

//---------------------------------------------------------------------------
using namespace ee;

//---------------------------------------------------------------------------
#pragma argsused
int main(int argc, char* argv[])
{
	int Arg = 1;
	char *Input = NULL;
	char *Output = NULL;
	char *UpdateMedia = NULL;
	int BuiltInIndex = -1;
	std::vector<std::string> Config;
	unsigned int Length = 0;

	while (Arg < argc)
	{
		if (!strcmp(argv[Arg], "-input"))
		{
			Arg++;
			if (Arg < argc)
			{
				Input = argv[Arg++];
			}
			else
			{
				break;
			}
		}
		else if (!strcmp(argv[Arg], "-output"))
		{
			Arg++;
			if (Arg < argc)
			{
				Output = argv[Arg++];
			}
			else
			{
				break;
			}
		}
		else if (!strcmp(argv[Arg], "-prolog"))
		{
			Arg++;
			if (Arg < argc)
			{
				Config.push_back(argv[Arg++]);
			}
			else
			{
				break;
			}
		}
		else if (!strcmp(argv[Arg], "-update_sources"))
		{
			Arg++;
			if (Arg < argc)
			{
				UpdateMedia = argv[Arg++];
			}
			else
			{
				break;
			}
		}
		else if (!strcmp(argv[Arg], "-built_in"))
		{
			Arg++;
			if (Arg < argc)
			{
				BuiltInIndex = atoi(argv[Arg++]);
			}
			else
			{
				break;
			}
		}
		else if (!strcmp(argv[Arg], "-length"))
		{
			Arg++;
			if (Arg < argc)
			{
				Length = atoi(argv[Arg++]);
			}
			else
			{
				break;
			}
		}
		else
		{
			break;
		}
	}

	if (Arg != argc/* || Input == NULL || Output == NULL*/)
	{
		std::cout << "Usage: " << argv[0] << " [-input input.xml] [-output output.xml]" << std::endl << std::endl;
		std::cout << "Available options:" << std::endl;
		std::cout << "-prolog file                consult prolog source" << std::endl;
		std::cout << "-update_sources directory" << std::endl;
		std::cout << "-built_in number            use built in prolog source" << std::endl;
		std::cout << "-length duration" << std::endl;
		return -1;
	}

	try
	{
		PL_action(PL_ACTION_GUIAPP, FALSE);

		int pargc = 0;
		char *pargv[10];
		pargv[pargc++] = "libpl.dll";
		pargv[pargc++] = "-L32m";
		pargv[pargc++] = "-G32m";
		pargv[pargc++] = "-T32m";
		pargv[pargc++] = "-A32m";
		pargv[pargc++] = "-nosignals";
		PlEngine *Prolog = new PlEngine(pargc, pargv);

		PL_set_engine(PL_ENGINE_MAIN, NULL);

		char *BuiltIn = NULL;
		if (BuiltInIndex >= 0)
		{
			TPrologSources PrologSources;
			BuiltIn = PrologSources.GetSource(BuiltInIndex);
		}

		if (BuiltIn)
		{
			PlFrame frame;
			char *buf = BuiltIn;
			int size = strlen(buf);

			int s = 0, p = 0;
			AnsiString c;
			while (p < size)
			{
				char b = buf[p];
				switch (s)
				{
				case 0:
					if (islower(b))
					{
						s++;
						c += b;
					}
					break;

				case 1:
					c += b;
					if (b == '\n')
					{
						s++;
					}
					break;

				case 2:
					if (islower(b))
					{
						PlCall("assert", PlCompound(c.c_str()));
						c = "";
						c += b;
						s = 0;
					}
					else
					{
						c += b;
						s--;
					}
					break;
				} //switch
				p++;
			} //while

			if (c != "")
			{
				PlCall("assert", PlCompound(c.c_str()));
			}
		} //if (BuiltIn)

		if (!Config.empty())
		{
			PlFrame frame;
			for (unsigned int c = 0; c < Config.size(); c++)
			{
				PlCall("consult", PlCompound(("\'" + NormalizeFilePathProlog(Config[c]) + "\'").c_str()));
			}
		}

		TImpExp ImpExpInput;
		if (Input)
		{
			ImpExpInput.Load(Input, false, false, -1);
//			if (UpdateMedia)
			{
//				ImpExpInput.UpdateMedia(UpdateMedia);
			}

			PlFrame frame;
			int MaxLength = 0;
			unsigned int Max = 0, Step = 0;
			for (TImpExp::TSources::iterator i = ImpExpInput.Sources.begin(); i != ImpExpInput.Sources.end(); i++)
			{
				std::stringstream c;
				c << "camera(" << i->Camera << ", " << i->Width << ", " << i->Height << ")";
				PlCall("assert", PlCompound(c.str().c_str()));
				if (i->Length > MaxLength)
				{
					MaxLength = i->Length;
				}

				TAVFile::TFrameRate fr = i->FrameRate;
				unsigned int l = TAVFile::Round((1000.0 * double(fr.Scale) * double(i->Length)) / double(fr.Rate)) + i->Offset;
				if (l > Max)
				{
					Max = l;
				}
				unsigned int s = TAVFile::Round((1000.0 * double(fr.Scale)) / double(fr.Rate));
				if ((Step == 0) || (Step > s))
				{
					Step = s;
				}
			}

			if (Step == 0)
			{
				Step = 1;
			}

			if (Length > 0 && Max > Length)
			{
				Max = Length;
			}

			PlCall("assert", PlCompound("begin(0)"));
			PlCall("assert", PlCompound(("end(" + IntToStr(Max) + ")").c_str()));
			PlCall("assert", PlCompound(("step(" + IntToStr(Step) + ")").c_str()));

			for (unsigned int t = 0; t < ImpExpInput.EventTypes.size(); t++)
			{
				TEvents::iterator i = ImpExpInput.Events.begin();
				while (i != ImpExpInput.Events.end())
				{
					TEvent *m = *i;
					if (m->ID == (int)t)
					{
						std::stringstream h;
						h << "event_" << TImpExp::AnsiString2Prolog(ImpExpInput.EventTypes[m->ID].Desc, true).c_str();
						h << "(" << m->Time << ", [";

						for (unsigned int j = 0; j < m->Parameters.size(); j++)
						{
							if (j > 0)
							{
								h << ", ";
							}
							h << "(" << TImpExp::AnsiString2Prolog(m->Parameters[j].Name).c_str() << ", " <<
							  TImpExp::AnsiString2Prolog(m->Parameters[j].Value).c_str() << ")";
						}

						h << "]).\n";
						PlCall("assert", PlCompound(h.str().c_str()));
						/*
						AnsiString h = "event_" + TImpExp::AnsiString2Prolog(ImpExpInput.EventTypes[m->ID].Desc, true);
						AnsiString EventLine;
						EventLine.cat_printf("%s(%d, [", h.c_str(), m->Time);
						for (unsigned int j = 0; j < m->Parameters.size(); j++)
						{
							if (j > 0)
							{
								EventLine.cat_printf(", ");
							}
							EventLine.cat_printf("(%s, %s)", TImpExp::AnsiString2Prolog(m->Parameters[j].Name).c_str(), TImpExp::AnsiString2Prolog(m->Parameters[j].Value).c_str());
						}
						EventLine.cat_printf("]).\n");
						PlCall("assert", PlCompound(EventLine.c_str()));
						*/
					}
					i++;
				}
			}
		}

		TImpExp ImpExpOutput;
		ImpExpOutput.Sources = ImpExpInput.Sources;

		// inicializace
		{
			PlFrame frame;
			PlCall("init");

			int Begin, End, Step;
			Begin = 0; End = 0; Step = 1;
			{
				PlTermv av(1);
				PlQuery q("begin", av);
				if (q.next_solution())
				{
					Begin = StrToInt((char*)av[0]);
				}
			}
			{
				PlTermv av(1);
				PlQuery q("end", av);
				if (q.next_solution())
				{
					End = StrToInt((char*)av[0]);
				}
			}
			{
				PlTermv av(1);
				PlQuery q("step", av);
				if (q.next_solution())
				{
					Step = StrToInt((char*)av[0]);
				}
			}

			// export nastaveni
			{
				PlFrame f;
				PlTermv av(1);
				PlQuery q("setup_events", av);
				if (q.next_solution())
				{
					TParameters Parameters;
					AnsiString s;
					PlTail l(av[0]);
					PlTerm e;
					while(l.next(e))
					{
						if (e.arity() == 2)
						{
							TParameter p((char*)e[1], (char*)e[2]);
							Parameters.push_back(p);
						}
						s = s + (char *)e + " ";
					}

					TEvent *ev = new TEvent();
					ev->Time = 0;
					ev->ID = ImpExpOutput.GetDefaultMessageID(TImpExp::detSetup);
					ev->Parameters = Parameters;
					ImpExpOutput.InsertMessage(ev);
				}
			}

			// hlavni smycka
			int Time = Begin;
			while (Time < End)
			{
				PlFrame f;
				PlTermv av(2);
				av[0] = Time;
				PlQuery q("main_step", av);
				if (q.next_solution())
				{
					TParameters Parameters;
					AnsiString s;
					PlTail l(av[1]);
					PlTerm e;
					while (l.next(e))
					{
						if (e.arity() == 2)
						{
							TParameter p((char*)e[1], (char*)e[2]);
							Parameters.push_back(p);
						}
						s = s + (char *)e + " ";
					}

					TEvent *ev = new TEvent();
					ev->Time = Time;
					ev->ID = ImpExpOutput.GetDefaultMessageID(TImpExp::detOutput);
					ev->Parameters = Parameters;
					ImpExpOutput.InsertMessage(ev);
				}
				Time += Step;
			}
		}

		if (Output)
		{
			ImpExpOutput.Save(Output);
		}

		PL_set_engine(PL_ENGINE_MAIN, NULL);
		delete Prolog;
	}

	catch (TError &E)
	{
		std::cerr << "Error: " << E.Message << std::endl;
		return -1;
	}
	catch (PlError &E)
	{
		std::cerr << "Prolog error: " << E.message << std::endl;
		return -1;
	}
	catch (PlException &E)
	{
		std::cerr << "Prolog exception: " << (char*)E << std::endl;
		return -1;
	}
	catch (...)
	{
		std::cerr << "Unknown Error" << std::endl;
		return -1;
	}

	return 0;
}

//---------------------------------------------------------------------------

