// ---------------------------------------------------------------------------
// 
//   Lily Engine alpha
// 
//   Copyright (c) 2010 Clifford Jolly
//     http://cliffjolly.com
//     cliff@cliffjolly.com
// 
// ---------------------------------------------------------------------------
// 
//   This program is free software; you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation; either version 2 of the License, or
//   (at your option) any later version.
//  
//   This program is distributed in the hope that it will be useful, but
//   WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//   General Public License for more details.
// 
//   You should have received a copy of the GNU General Public License
//   along with this program; if not, write to the Free Software
//   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
//   USA
// -------------------------- END HEADER -------------------------------------

///////////////////////////////////////////////////////////////////////
// NOBODY SHOULD EVER LOOK AT THIS EVER.
// Except me.
//
// But seriously, this is all ugly-as-hell, prototype-only code.
///////////////////////////////////////////////////////////////////////

#include <cassert>
#include <iostream>
#include <sstream>
#include <cmath>
using namespace std;

#include "../console/console.h"
using namespace Console;

#include <GL/glew.h>
#include <SDL.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include "../3rdparty/tinyxml/tinyxml.h"

#include "../malfont/malfont.h"
#include "../malfont/fontmanager.h"

#include "../texturemanager/texturemanager.h"

#include "../math/matrix.h"
#include "../math/vector3d.h"
#include "../math/vector2d.h"

#include "../filesystem/filesystem.h"

#include "widget.h"
#include "widgettext.h"
#include "widgetbutton.h"
#include "widgetpaddedbox.h"
#include "widgetcontainer.h"
#include "widgettransform.h"
#include "widgetscrollable.h"

extern "C" {
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}

void projectOntoGLZPlane(float x, float y, float *rx, float *ry);
Vector2D windowToViewport(const Vector2D &windowCoords);
Widget *xmlLoadedWidgets = NULL;

// TODO: Make the key repeat stuff nicer.

struct KeyRepeatRecord {
	int timeout;
	int key;
	char converted;
};

vector<KeyRepeatRecord*> repeatKeys;

void addKeyRepeat(int k, char converted) {
	
	for(unsigned int i = 0; i < repeatKeys.size(); i++) {
		if(repeatKeys[i]->key == k && repeatKeys[i]->converted == converted) return;
	}
	
	KeyRepeatRecord *rec = new KeyRepeatRecord();
	rec->converted = converted;
	rec->key = k;
	rec->timeout = 10;
	repeatKeys.push_back(rec);
}

void removeKeyRepeat(int k) {
	for(unsigned int i = 0; i < repeatKeys.size(); i++) {
		if(repeatKeys[i]->key == k) {
			delete repeatKeys[i];
			repeatKeys[i] = repeatKeys[repeatKeys.size() - 1];
			repeatKeys.erase(repeatKeys.end() - 1);
			return;
		}
	}
}

bool testCB(void *junk) {
	out("test") << "Whee!" << endl;
	return false;
}

int main(int argc, char *argv[]) {

	outSetAttribs("error",    "31;1");
	outSetAttribs("warning",  "33;1");
	outSetAttribs("test",     "32;1");
	outSetAttribs("graphics", "34;1");
	outSetAttribs("lua",      "36;1");

	outSetPrefix("graphics", "Graphics: ");
	outSetPrefix("error",    "Error:    ");
	outSetPrefix("warning",  "Warning:  ");
	outSetPrefix("test",     "Test:     ");
	outSetPrefix("lua",      "Lua:      ");

	out("error")    << "Errors look like this" << endl;
	out("warning")  << "Warnings look like this" << endl;
	out("test")     << "Generic test stuff looks like this" << endl;
	out("graphics") << "Graphics info looks like this" << endl;
	out("lua")      << "Lua debugging crap looks like this" << endl;

	outSetEnabled("graphics", false);

	out("main") << "----------------------------------------------------------" << endl;
	
	SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
	SDL_SetVideoMode(1024, 768, 32, SDL_OPENGL);
	SDL_EnableUNICODE(1);
	
	glEnable(GL_BLEND);
	//glEnable(GL_SCISSOR_TEST);
	glEnable(GL_TEXTURE_2D);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glClearStencil(0);
	
	glewInit();
	
	lua_State *luaState = luaL_newstate();
	luaL_openlibs(luaState);
	//widgetLuaState = luaState;
	
	luaL_loadstring(luaState, "print(\"Fuck yeah, bitches!\");");
	if(lua_type(luaState, -1) == LUA_TSTRING) {
		cout << lua_tostring(luaState, -1) << endl;
		exit(1);
	} else if(lua_type(luaState, -1) == LUA_TFUNCTION) {
		lua_pcall(luaState, 0, LUA_MULTRET, 0);
	}
	
	widgetInitLua(luaState);
	
	// TODO: Do this with lua_load, which also lets us load from
	//   magical archives.
	if(luaL_loadfile(luaState, "test.lua")) {
		out("error") << lua_tostring(luaState, -1) << endl;
		lua_pop(luaState, 1);
	}
	
	if(lua_pcall(luaState, 0, LUA_MULTRET, 0)) {
		out("error") << lua_tostring(luaState, -1) << endl;
		lua_pop(luaState, 1);
	}
	
	bool quit = false;

	//delete widgetRoot;

	//FileSystem::addArchiveForSearch("teststuff1.poo");
/*
	//xmlLoadedWidgets = widgetRoot;
	xmlLoadedWidgets = new WidgetButton();
	//xmlLoadedWidgets->setMinHeight(48);
	//xmlLoadedWidgets->setMinWidth(100);
	xmlLoadedWidgets->setBackgroundImage("np_ff1.ninepatch");
	
	WidgetButton *button = dynamic_cast<WidgetButton *>(xmlLoadedWidgets);
	if(button) {
		button->backgroundActive = "np_ff1.ninepatch";
		button->backgroundMouseOver = "test_np.ninepatch";
		button->backgroundDefault = "asdf.tga";
	}
	*/
	if(!widgetRoot) {
		out("warning") << "No root widget. Falling back on XML." << endl;
		xmlLoadedWidgets = widgetCreateFromXMLFile("textui.xml");
		if(!xmlLoadedWidgets) return 1;
	}
	
	//xmlLoadedWidgets->setNativeEventHandler("WIDGETEVENT_MOUSEDOWN", testCB, NULL);
	
	/*WidgetTransform *wtf = new WidgetTransform();
	wtf->transform[12] = 0;
	wtf->setChild(xmlLoadedWidgets);
	xmlLoadedWidgets = wtf;
	wtf->setWidth(1024);
	wtf->setHeight(768);
	
	WidgetTransform *wtf2 = new WidgetTransform();
	wtf->transform[3] = 0.001;
	wtf2->setChild(xmlLoadedWidgets);
	xmlLoadedWidgets = wtf2;
	wtf2->setWidth(1024);
	wtf2->setHeight(768);*/
	
	xmlLoadedWidgets = widgetRoot;
	
	if(!widgetRoot) {
		out("error") << "No widgets!" << endl;
		exit(1);
	}
	
	//float yangle = 0;
	float zangle = 0;
	//float xangle = 0;
	
	MalFont *font = FontManager::get()->getFont("defaultfont.tga");
	font->setVerticalSquish(5);
	font = FontManager::get()->getFont("orangething2.tga");
	//font->setVerticalSquish(16);
	//font->setSquish(4);

	bool shiftHeld = false;
	bool ctrlHeld = false;
	bool altHeld = false;
		
	WidgetEvent initialFocusEvent;
	initialFocusEvent.type = WIDGETEVENT_FOCUSENTER;
	xmlLoadedWidgets->handleEvent(&initialFocusEvent);
		
	while(!quit) {
		//xmlLoadedWidgets->setWidth(xmlLoadedWidgets->getMinWidth());
		//xmlLoadedWidgets->setHeight(xmlLoadedWidgets->getMinHeight());
		
		unsigned int startTime = SDL_GetTicks();
		

		//glClearColor(0, 1, 0, 1);
		
		glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();

		glColor4f(1, 1, 1, 1);
		glDisable(GL_TEXTURE_2D);
		glBegin(GL_TRIANGLES);
			
			glColor4f(1, 0, 1, 0.5);	
			glVertex2f(0, 0);
			
			glColor4f(0, 1, 1, 0.5);
			glVertex2f(1, 0);
			
			glColor4f(1, 1, 0, 0.5);
			glVertex2f(0, 1);
		glEnd();
		
		//glMatrixMode(GL_PROJECTION);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		gluOrtho2D(0, 1024, 768, 0);
		//gluPerspective(90, 4.0 / 3.0, 0.001, 1000.0);
		
		glColor4f(1, 1, 1, 1);
		
		
		/*WidgetText *text = (WidgetText*)((WidgetPaddedBox*)(((WidgetContainer*)((WidgetPaddedBox*)xmlLoadedWidgets)->getChild())->getSlot(1, 1)))->getChild();
		//text->setWordWrap(0);
		int r = rand() % 16;
		ostringstream ostr;
		for(int i = 0; i < r; i++) {
			ostr << i % 10;
		}
		//text->setText(ostr.str());*/
		
		
		/*if(xmlLoadedWidgets->isSizeDirty()) {
			xmlLoadedWidgets->setWidth(0);
			xmlLoadedWidgets->setHeight(0);
		}*/
		xmlLoadedWidgets->setWidth((int)(sin(float(SDL_GetTicks()) / 1000.0) * 512) + 512);
		xmlLoadedWidgets->setHeight((int)(cos(float(SDL_GetTicks()) / 1000.0) * 384) + 384);
		
		WidgetSingleParent *singleParent = dynamic_cast<WidgetSingleParent*>(xmlLoadedWidgets);
		if(singleParent) {
			WidgetScrollable *scrollable = dynamic_cast<WidgetScrollable*>(singleParent->getChild());
			if(scrollable) {
				scrollable->setOffsetX((1 + sin(float(SDL_GetTicks()) / 1000.0)) * scrollable->getMaxOffsetX() / 2);
				scrollable->setOffsetY((1 + cos(float(SDL_GetTicks()) / 1000.0)) * scrollable->getMaxOffsetY() / 2);
			}
		}
		
		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		
		/*float stupidMatrix[] = {
			1.0, 0.0, 0.0, 0.001,
			0.0, 1.0, 0.0, 0.001,
			0.0, 0.0, 1.0, 0.001,
			0.0, 1.0, 0.0, 1.0,
		};*/		
		//glMultMatrixf(stupidMatrix);
		
		//glRotatef(float(SDL_GetTicks()) / 100.0, 0, 1, 0);
		//glRotatef(float(SDL_GetTicks()) / 10.0, 0, 0, 1);
		
		//glTranslatef(0, 0, -100);
		//glTranslatef(100, 100, 0);
		
		/*glRotatef(xangle, 1, 0, 0);
		glRotatef(yangle, 0, 1, 0);
		glRotatef(zangle, 0, 0, 1);		
		glTranslatef(-100, -100, 0);*/
		
		glEnable(GL_STENCIL_TEST);
		
		xmlLoadedWidgets->draw();
		
		glDisable(GL_STENCIL_TEST);

		debugDrawOffsetX = 0;
		debugDrawOffsetY = 0;
		
		SDL_Event event = {0};
		
		while(SDL_PollEvent(&event)) {
			WidgetEvent widgetEvent;
			memset(&widgetEvent, 0, sizeof(widgetEvent));
			
			Vector2D convertedCoords1;
			Vector2D convertedCoords2;

			if(event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_TAB) {
				
				widgetEvent.type = shiftHeld ? WIDGETEVENT_FOCUSPREV : WIDGETEVENT_FOCUSNEXT;
				xmlLoadedWidgets->handleEvent(&widgetEvent);
				
			} else switch(event.type) {
				case SDL_QUIT:
					quit = true;
					break;
				
				case SDL_KEYDOWN:
				case SDL_KEYUP:
					{
						bool shiftSwitch = event.type == SDL_KEYDOWN;
						if(event.key.keysym.sym == SDLK_LSHIFT || event.key.keysym.sym == SDLK_RSHIFT) {
							shiftHeld = shiftSwitch;
						}
						if(event.key.keysym.sym == SDLK_LCTRL  || event.key.keysym.sym == SDLK_RCTRL) {
							ctrlHeld = shiftSwitch;
						}
						if(event.key.keysym.sym == SDLK_LALT  || event.key.keysym.sym == SDLK_RALT) {
							altHeld = shiftSwitch;
						}
					}
					
					// Hello, this is why switch() was invented.
					// TODO: Fix this shit.
					if(event.key.keysym.sym == SDLK_LEFT) {
						widgetEvent.keyboard.keyCode = WIDGET_KEY_LEFT;
					} else if(event.key.keysym.sym == SDLK_RIGHT) {						
						widgetEvent.keyboard.keyCode = WIDGET_KEY_RIGHT;
					} else if(event.key.keysym.sym == SDLK_UP) {
						widgetEvent.keyboard.keyCode = WIDGET_KEY_UP;
					} else if(event.key.keysym.sym == SDLK_DOWN) {
						widgetEvent.keyboard.keyCode = WIDGET_KEY_DOWN;
					} else if(event.key.keysym.sym == SDLK_HOME) {
						widgetEvent.keyboard.keyCode = WIDGET_KEY_HOME;
					} else if(event.key.keysym.sym == SDLK_END) {
						widgetEvent.keyboard.keyCode = WIDGET_KEY_END;
					} else if(event.key.keysym.sym == SDLK_PAGEDOWN) {
						widgetEvent.keyboard.keyCode = WIDGET_KEY_PAGEDOWN;
					} else if(event.key.keysym.sym == SDLK_PAGEUP) {
						widgetEvent.keyboard.keyCode = WIDGET_KEY_PAGEUP;
					} else if(event.key.keysym.sym == SDLK_RETURN) {
						widgetEvent.keyboard.keyCode = WIDGET_KEY_RETURN;
						widgetEvent.keyboard.convertedKey = event.key.keysym.unicode & 0xff;
					} else if(event.key.keysym.sym == SDLK_SPACE) {
						widgetEvent.keyboard.keyCode = WIDGET_KEY_SPACE;
						widgetEvent.keyboard.convertedKey = event.key.keysym.unicode & 0xff;
					} else if(!(event.key.keysym.unicode & 0xff00)) {
						widgetEvent.keyboard.convertedKey = event.key.keysym.unicode & 0xff;
						
						// SDL is weird and shifts ascii stuff around if ctrl is held.
						if(ctrlHeld && widgetEvent.keyboard.convertedKey) widgetEvent.keyboard.convertedKey += ('a' - 1);
					}

					// TODO: Get rid of this.
					if(event.key.keysym.sym == SDLK_a) {
						zangle += 15;
					}
					if(event.key.keysym.sym == SDLK_z) {
						zangle -= 15;
					}
					
					// TODO: Get rid of this.
					if(event.key.keysym.sym == SDLK_r) {
						// Force texture reload.
						TextureManager *manager = TextureManager::get();
						
						// Excessive? Good thing it's just testing code.
						manager->cleanOldTextures(0);
						manager->cleanOldTextures(0);
						manager->cleanOldTextures(0);
						manager->cleanOldTextures(0);
						manager->cleanOldTextures(0);
						manager->cleanOldTextures(0);
						manager->cleanOldTextures(0);
					}
					
					if(event.type == SDL_KEYDOWN) {
						widgetEvent.type = WIDGETEVENT_KEYDOWN;
						if(!shiftHeld && !altHeld && !ctrlHeld) {
							addKeyRepeat(widgetEvent.keyboard.keyCode, widgetEvent.keyboard.convertedKey);
						}
					} else {
						widgetEvent.type = WIDGETEVENT_KEYUP;
						removeKeyRepeat(widgetEvent.keyboard.keyCode);
					}
					
					widgetEvent.keyboard.alt = altHeld;
					widgetEvent.keyboard.ctrl = ctrlHeld;
					widgetEvent.keyboard.shift = shiftHeld;
					xmlLoadedWidgets->handleEvent(&widgetEvent);
					
					break;
					
				case SDL_MOUSEMOTION:
					widgetEvent.type = WIDGETEVENT_MOUSEMOTION;
					
					convertedCoords1 = widgetWindowToViewport(Vector2D(event.motion.x, event.motion.y));
					convertedCoords2 = widgetProjectOntoGLZPlane(convertedCoords1);
					widgetEvent.mouse.x = convertedCoords2.x;
					widgetEvent.mouse.y = convertedCoords2.y;
					
					xmlLoadedWidgets->handleEvent(&widgetEvent);
					break;

				case SDL_MOUSEBUTTONDOWN:
				case SDL_MOUSEBUTTONUP:
					widgetEvent.type = (event.type == SDL_MOUSEBUTTONDOWN ? WIDGETEVENT_MOUSEDOWN : WIDGETEVENT_MOUSEUP);
					
					convertedCoords1 = widgetWindowToViewport(Vector2D(event.button.x, event.button.y));
					convertedCoords2 = widgetProjectOntoGLZPlane(convertedCoords1);
					widgetEvent.mouse.x = convertedCoords2.x;
					widgetEvent.mouse.y = convertedCoords2.y;
					widgetEvent.mouse.button = event.button.button - 1;
					xmlLoadedWidgets->handleEvent(&widgetEvent);
					
					break;
					
				default:
					break;
			}
		}
		
		// Handle repeating keys.
		for(unsigned int i = 0; i < repeatKeys.size(); i++) {
			if(repeatKeys[i]->timeout == 0) {
				WidgetEvent widgetEvent;
				memset(&widgetEvent, 0, sizeof(widgetEvent));
				widgetEvent.type = WIDGETEVENT_KEYREPEAT;
				widgetEvent.keyboard.keyCode = repeatKeys[i]->key;
				widgetEvent.keyboard.convertedKey = repeatKeys[i]->converted;
				widgetEvent.keyboard.alt = altHeld;
				widgetEvent.keyboard.ctrl = ctrlHeld;
				widgetEvent.keyboard.shift = shiftHeld;
				xmlLoadedWidgets->handleEvent(&widgetEvent);
			} else {
				repeatKeys[i]->timeout--;
			}
		}
	
		widgetCleanupDeletedWidgets();
	
		glPopMatrix();
		
		glColor4f(1, 1, 1, 1);

		SDL_GL_SwapBuffers();
		
		int delay = 30 - (int)(SDL_GetTicks() - startTime);
		if(delay > 0) SDL_Delay(delay);
	}

	FileSystem::clearSearchArchives();

	delete xmlLoadedWidgets;
	
	widgetCleanupDeletedWidgets();

	SDL_Quit();
	
	lua_close(luaState);
	
}

