// nettool.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "thread.h"
#include "httpd.h"
#include "apptasker.h"
#include "config.h"
#include "analysis.h"
#include "PredictionHub.h"
#include "VerletPredictor.h"
#include "AccelPredictor.h"
#include "ZeroPredictor.h"
#include "StatsCollector.h"
#include "DirectionalPredictor.h"
#include "BuildSegmentedCompressionSpace.h"
#include "tinyxml/tinyxml.h"
#include "SnappingAnalyzer.h"

int g_mutexLocks;
CMutex g_mutex;

class CHelloWorld : public IServlet
{
public:
	virtual void Get( CHTTPOutput * pOutput, const ParamsMap& )
	{
		pOutput->Write("<html><body>Hello world</body></html>");
	}
};

void HSLtoRGB(float hsl[3], float rgb[3])
{
	static const float c1d3 = 1.0f/3.0f;
	static const float c1d6 = 1.0f/6.0f;
	static const float c2d3 = 2.0f/3.0f;
	
	const float& h = hsl[0];
	const float& s = hsl[1];
	const float& l = hsl[2];
	
	float t1,t2,t3[3];
	if(hsl[2]<0.5f)
		t2 = l*(1.0f+s);
	else
		t2 = l+s-(l*s);
	t1 = 2.0f*l - t2;

	t3[0] = h + c1d3;
	if(t3[0]>1.0f)
		t3[0] -= 1.0f;
	t3[1] = h;
	t3[2] = h - c1d3; 
	if(t3[2]<0.0f)
		t3[2] += 1.0f;
	for(int i=0;i<3;i++)
	{
		if(t3[i]<c1d6)
			rgb[i] = t1 + ((t2-t1)*6.0f*t3[i]);
		else if(t3[i]<0.5f)
			rgb[i] = t2;
		else if(t3[i]<c2d3)
			rgb[i] = t1 + ((t2-t1)*(c2d3-t3[i])*6.0f);
		else
			rgb[i] = t1;
	}
}

class CSpectrum : public IServlet
{
public: 
	virtual void Get( CHTTPOutput * pOutput, const ParamsMap& )
	{
		int W = 600;
		int H = 10;
		CImage<iRGB> img(W,H,iRGB(0,0,0));
		float hsl[3];
		hsl[1] = 1.0f;
		for (int i=0; i<H; i++)
		{
			hsl[2] = 0.5f;
			for (int j=0; j<W; j++)
			{
				float t = float(j)/float(W);
				hsl[0] = 0.7f*t+0.3f;
				t = pow(t,0.3f);
				float rgb[3];
				HSLtoRGB(hsl,rgb);
						
				img(j,i).r = int(t*rgb[0]*255.0f);
				img(j,i).g = int(t*rgb[1]*255.0f);
				img(j,i).b = int(t*rgb[2]*255.0f);
			}
		}
		pOutput->WriteImage(img);
	}	
};

class CGradient : public IServlet
{
public: 
	virtual void Get( CHTTPOutput * pOutput, const ParamsMap& )
	{
		int W = 1000;
		int H = 10;
		CImage<iRGB> img(W,H,iRGB(0,0,0));
		for (int i=0; i<H; i++)
		{
			for (int j=0; j<W; j++)
			{
				float t = float(j)/float(W);
				float tt = pow(t,0.2f);										
				img(j,i).r = int(t*255.0f);
				img(j,i).g = int(tt*255.0f);
				img(j,i).b = int(t*255.0f);
			}
		}
		pOutput->WriteImage(img);
	}	
};

class CRed : public IServlet
{
public:
	virtual void Get( CHTTPOutput * pOutput, const ParamsMap& )
	{
		int W = 256;
		int H = 256;
		CImage<iRGB> img(W,H,iRGB(0,0,0));
		for (int i=0; i<H; i++)
		{
			for (int j=0; j<W; j++)
			{
				img(j,i).r = i;
				img(j,i).g = j;
				img(j,i).b = 255 - (i+j)/2;
			}
		}
		pOutput->WriteImage(img);
	}
};

class CSnappingPlot : public IServlet
{
public:
	virtual void Get( CHTTPOutput * pOutput, const IServlet::ParamsMap& params)
	{
		CSnappingAnalyzer analyzer("snapping.xml");

		ParamsMap::const_iterator it = params.find("group");
		uint32 group = it != params.end() ? StringToKey(it->second) : 0;
		it = params.find("opaque");
		bool opaque = it != params.end() ? true : false;
		CImage<iRGB> img(512, 512);
		analyzer.Plot(img, group, opaque);
		pOutput->WriteImage(img);
	}
};

static void PredictionTableBuilder(void*)
{
	CThread::SetName("PredictionTableBuilder");

	std::vector<SPredictorParams> predictors;

	// 1. read in the CompressionPolicy.xml file, collect predictor parameters for respective policies
	SConfig config = GetConfig();
	TiXmlHandle hdl(&config.policiesXml);
	TiXmlElement* policies = hdl.FirstChild("CompressionPolicy").ToElement();
	if (!policies)
		throw std::runtime_error("no policy nodes");
	for (TiXmlNode* policy = policies->FirstChildElement("Policy"); policy; policy = policy->NextSibling("Policy"))
	{
		const char* policyName = policy->ToElement()->Attribute("name");
		if (!policyName)
			continue;
		TiXmlNode* predictor = policy->ToElement()->FirstChildElement("Predictor");
		if (!predictor)
			continue;
		SPredictorParams params;
		const char* predictorName = predictor->ToElement()->Attribute("name");
		params.name = predictorName ? predictorName : "ZeroPredictor";
		const char* predictorDim = predictor->ToElement()->Attribute("dim");
		params.dim = predictorDim ? atoi(predictorDim) : 0;
		const char* predictorBits = predictor->ToElement()->Attribute("bits");
		params.bits = predictorBits ? atoi(predictorBits) : 0;
		const char* predictorSteps = predictor->ToElement()->Attribute("steps");
		params.steps = predictorSteps ? atoi(predictorSteps) : 0;
		const char* predictorMaxValue = predictor->ToElement()->Attribute("maxValue");
		params.maxValue = predictorMaxValue ? atof(predictorMaxValue) : 0.0f;
		params.input = KeyToString(StringToKey(string(policyName))) + "_*.dat";
		params.policyName = string(policyName); // + ".xml";
		params.pHub = new CPredictionHub;
		predictors.push_back(params);
	}

	// 2. for each policy, use the predictor parameters to generate corresponding table
	bool firstRound = true;
	while (true)
	{
		Sleep(60*60*1000); // 1 hour

		for (size_t i = 0; i < predictors.size(); ++i)
		{
			_finddata_t fd;
			intptr_t hdl = _findfirst(predictors[i].input.c_str(), &fd);
			if (hdl == -1)
				continue;
			int r = -1;
			_smart_ptr<CPredictionHub> pHub = predictors[i].pHub;
			bool firstTime = firstRound;
			do 
			{
				pHub->ProcessData(fd.name, predictors[i].name, firstTime, "", "");
				firstTime = false;

				std::string ren = fd.name;
				size_t i = ren.find(".dat");
				ren.replace(i, 4, ".old");
				rename(fd.name, ren.c_str());

				r = _findnext(hdl, &fd);
			} while(r == 0);
			_findclose(hdl);

			SYSTEMTIME st;
			GetSystemTime(&st);
			std::stringstream filename;
			filename << predictors[i].policyName << "_" << st.wYear << "_" << st.wMonth << "_" << st.wDay << "_" << st.wHour << "-" << st.wMinute << "-" << st.wSecond << ".xml";
			FILE* file = fopen(filename.str().c_str(), "w");
			if (!file)
			{
				printf("ERROR: failed opening %s for writing\n", filename.str().c_str());
				continue;
			}
			fprintf(file, "<ctab d=\"%d\" b=\"%d\" s=\"%d\" m=\"%f\">\n", predictors[i].dim, predictors[i].bits, predictors[i].steps, predictors[i].maxValue);
			BuildSegmentedCompressionSpace( file, pHub->GetSamples(), pHub->GetNumSamples(), predictors[i].maxValue / (1<<predictors[i].bits), predictors[i].dim, predictors[i].steps );
			fprintf(file, "</ctab>\n");
			fclose(file);
		}

		firstRound = false;
	}
}

int main(int argc, const char* argv[])
{
	WSAData dat;
	WSAStartup( MAKEWORD(1,1), &dat );

	if (argc <= 1)
	{
		CAppTasker tasker;
		CThread analyser(Analyser, NULL);
		CThread garbageCollector(GarbageCollector, NULL);
		CThread predictionTableBuilder(PredictionTableBuilder, NULL);

		CHTTPD httpd;
		httpd.Register( "/helloworld.htm", new CHelloWorld );
		httpd.Register( "/predict.htm", new CPredictionHub );
		httpd.Register( "/red.png", new CRed );
		httpd.Register( "/spectrum.png", new CSpectrum );
		httpd.Register( "/gradient.png", new CGradient );
		httpd.Register( "/snapping.htm", new CSnappingPlot );
		RegisterConfigPages(&httpd);
		httpd.Run(8000);
	}
	else if (argc == 7)
	{
		// nettool.exe 3 24 3 4095 wrld_20060211_15-13-28-44.dat Directional > wrld.xml
		int dim = atoi(argv[1]);
		int bits = atoi(argv[2]);
		int steps = atoi(argv[3]);
		float maxVal = atof(argv[4]);
		const char * filename = argv[5];
		const char * predictor = argv[6];

		CPredictionHub hub;
		hub.ProcessData( filename, predictor,true, "", "" );
		fprintf(stdout, "<ctab d=\"%d\" b=\"%d\" s=\"%d\" m=\"%f\">\n", dim, bits, steps, maxVal);
		BuildSegmentedCompressionSpace( stdout, hub.GetSamples(), hub.GetNumSamples(), maxVal / (1<<bits), dim, steps );
		fprintf(stdout, "</ctab>\n");
	}

	WSACleanup();

	return 0;
}
