#include "lgi/common/Lgi.h"
#include "lgi/common/Net.h"
#include "lgi/common/NetTools.h"
#include "mc4.h"
#include "WebThread.h"

#define WEB_PORT			2222

struct WebThreadPriv;
typedef LHashTbl<ConstStrKey<char>,LString> StrMap;
typedef LString (*pPageCallback)(WebThreadPriv *, StrMap &);
LString OnRootDoc(WebThreadPriv *d, StrMap &Var);
LString OnShutdownDoc(WebThreadPriv *d, StrMap &Var);
LString EditIa(WebThreadPriv *d, StrMap &Var);

struct Page
{
	const char *Name;
	pPageCallback Cb;
};

Page Pages[] =
{
	{"/", OnRootDoc},
	{"/shutdown.html", OnShutdownDoc},
	{"/edit_ia.html", EditIa},
	{0, 0}
};

struct WebThreadPriv
{
	LViewI *v;
	Mc4Shared *Shared;
	bool Loop;
	
	WebThreadPriv(Mc4Shared *shared, LViewI *view)
	{
		Shared = shared;
		v = view;
		Loop = true;
	}
	
	bool OnRequest(LString &Resp, LString Req)
	{
		LString::Array Hdr = Req.Split("\r\n", 1);
		LString::Array r = Hdr[0].SplitDelimit(" \r\t\n");
		if (r.Length() < 3)
			return false;
		LAutoString Host(Hdr.Length() > 1 ? InetGetHeaderField(Hdr[1], "Host") : NULL);
		LString::Array Uri = r[1].Split("?", 1);
		
		StrMap Vars;
		LString Html;
		LString Status = "500 Server Error";
		Page *p = NULL;
		printf("Uri=%s\n", Uri.First().Get());
		for (unsigned i=0; Pages[i].Name; i++)
		{
			if (Uri.First().Equals(Pages[i].Name))
			{
				p = Pages + i;
				break;
			}
		}
		if (p)
		{
			Status = "200 OK";
			
			if (Uri.Length() > 1)
			{
				LString::Array a = Uri[1].SplitDelimit("&");
				for (auto &s: a)
				{
					LString::Array v = s.Split("=", 1);
					if (v.Length() == 2)
						Vars.Add(v[0], v[1]);
					else
						LgiTrace("%s:%i - Error: only one part?\n", _FL);
				}
			}
			
			Html = p->Cb(this, Vars);
		}
		else		
		{
			// Page not found..
			Status = "404 Not Found";
			Html =	"<html>\n"
					"<body>404 - not found\n"
					"</body>\n"
					"</html>";
		}
		
		Resp.Printf("HTTP/1.0 %s\r\n"
					"Content-Type: text/html\r\n"
					"Content-Length: %i\r\n"
					"\r\n"
					"%s",
					Status.Get(),
					(int)Html.Length(),
					Html.Get());
		
		return true;
	}
};

/////////////////////////////////////////////////////////////////////////////////////
const char *PageHeader =	"<html>\n"
							"<head>\n"
							"<title>MC4</title>\n"
							"<style>\n"
							"td { background: white;\n"
							"}\n"
							"ul { margin-top: 0em;\n"
							"	margin-bottom: 0em;\n"
							"}\n"
							".btn {\n"
							"	font-family:\"dejavu sans\";\n"
							"	text-decoration:none;\n"
							"	color:black;\n"
							"	display:inline-block;\n"
							"	padding: 3px;\n"
							"	background:#ccc;\n"
							"}\n"
							"</style>\n"
							"</head>\n"
							"<body>\n";

const char *PageFooter =	"</body>\n"
							"</html>";

LString OnRootDoc(WebThreadPriv *d, StrMap &Var)
{
	LStringPipe p;
	p.Print("%s", PageHeader);

	auto Sh = d->Shared->Lock(_FL);
	
	p.Print("<h2>Preset %i</h2>\n", Sh->PresetIndex + Mc4Ui::DisplayOffset);	
	
	p.Print("<h3>Instant access buttons:</h3>"
			"<table style='background: #ccc; cellspacing: 1px;'><tr>\n");
	
	auto Press = Sh->Preset.GetTypePress();
	if (Press.Length() == 0)
		p.Print("<td>No IAs");
	else
		for (unsigned i=0; i<Press.Length(); i++)
		{
			auto ia = Press[i]; 
			p.Print("<td style='padding: 0px;'>\n"
					"<table cellspacing=0>\n"
					"	<tr>\n"
					"		<td style='padding: 3px;'><b>IA%i</b>\n"
					"		<td style='padding: 0px;'><a href='edit_ia.html?preset=%i&ia=%i' class='btn'>&#128393;...</a><br>\n"
					"	<tr>\n"
					"		<td style='padding: 0.6em;'>Colour: <span style='color: #%.2x%.2x%.2x;'>&#9632;</span><br>\n"
					"			Commands:<ul>\n",
					i, Sh->PresetIndex, i,
					ia->GetColour().r(),
					ia->GetColour().g(),
					ia->GetColour().b()
					);
			for (auto c: ia->Cmds)
			{
				switch (c.Id)
				{
					default:
						p.Print("<li> None\n");
						break;
					case MIDI_CMD_SCENE:
						p.Print("<li> Scene: %i\n", c.Param);
						break;
					case MIDI_CMD_CC:
						p.Print("<li> CC: %i\n", c.Param);
						break;
					case MIDI_CMD_BLOCK:
					{
						auto b = GetBlockByIndex(c.Param);
						if (b)
							p.Print("<li> Block: %s\n", b->Name);
						else
							p.Print("<li> Unknown block: %i\n", c.Param);
						break;
					}
				}
			}
			p.Print("</ul>\n"
					"</table>\n");
		}
	p.Print("\t</table>\n");

	p.Print("<h3>Actions:</h3>\n"
			"<a href='shutdown.html'>Shutdown</a>\n"
			"%s",
			PageFooter);
	
	return p.NewGStr();
}

LString OnShutdownDoc(WebThreadPriv *d, StrMap &Var)
{
	LString s;
	s.Printf("%sMC4 shutting down...\n"
			"%s\n",
			PageHeader,
			PageFooter);

	d->v->PostEvent(M_CLOSE);

	return s;
}

LString EditIa(WebThreadPriv *d, StrMap &Var)
{
	LString s;
	LString preset = Var.Find("preset");
	LString ia = Var.Find("ia");
	
	s.Printf("%sEdit IA: %s %s\n"
			"%s\n",
			PageHeader,
			preset.Get(),
			ia.Get(),
			PageFooter);

	return s;
}


/////////////////////////////////////////////////////////////////////////////////////
WebThread::WebThread(Mc4Shared *Shared, LViewI *View) : LThread("WebThread")
{
	d = new WebThreadPriv(Shared, View);	
	Run();
}

WebThread::~WebThread()
{
	d->Loop = false;
	while (!IsExited())
		LSleep(1);
	DeleteObj(d);	
}
	
int WebThread::Main()
{
	LSocket Listen;
	if (!Listen.Listen(WEB_PORT))
		LgiTrace("%s:%i - Can't listen on port %i.\n", _FL, WEB_PORT);

	while (d->Loop)
	{
		if (Listen.IsReadable(50))
		{
			LSocket In;
			if (Listen.Accept(&In))
			{
				LString Req;
				char Buf[256];
				ssize_t Rd;
				while ((Rd = In.Read(Buf, sizeof(Buf))) > 0)
				{
					Req += LString(Buf, Rd);
					if (Req.Find("\r\n\r\n"))
						break;
				}
				
				if (Req.Length() > 0)
				{
					LString Resp;
					d->OnRequest(Resp, Req);
					if (Resp)
					{
						auto Wr = In.Write(Resp.Get(), Resp.Length());
						LAssert(Wr == Resp.Length());
					}
				}
			}
		}
	}
	
	return 0;
}
