// ---------------------------------------------------------------------------
// 
//   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 -------------------------------------

#include "../hacks/winhacks.h"

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

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

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

#include <boost/bind.hpp>

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

#include "../image/image.h"
#include "../texturemanager/texturemanager.h"
#include "../texturemanager/texture.h"
#include "../filesystem/filesystem.h"
#include "../3rdparty/tinyxml/tinyxml.h"
#include "../tinyxml/xmlstuff.h"
#include "../string/string.h"

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

lua_State *widgetLuaState = NULL;
Widget* widgetRoot = NULL;

// Simple struct containing the string name of an event, and a pointer
// to the original event that caused it. Also the Widget that it came
// from.
struct WidgetLuaEventInfo {
	Widget *widget;
	const WidgetEvent *event;
	std::string eventName; // Can be a pseudo-event, too.
};
static std::vector<WidgetLuaEventInfo *> widgetLuaEventStack;

// --------------------------------------------------------------------
//  Widget global UIDs/references
// --------------------------------------------------------------------

// Use a 64-bit integer to make unique Widget references. In the future
// we could probably change this to some sort of string-based superint,
// but I'm not terribly worried about it right now.
static unsigned long long widgetUIDCounter = 0;

static std::map<std::string, Widget *> widgetUIDMap;

static std::string widgetMakeUID(Widget *w) {
	ostringstream str;
	str << widgetUIDCounter++;
	
	if(widgetUIDCounter == 0) {
		// Somehow, we managed to create 2^64 different Widgets over
		// the lifetime of the app. I don't even know what to do here.
		// Seriously. This should probably be an assert.
		
		// FIXME: Make it an assert?
		
		out("warning") << "Widget UID counter has wrapped around." << endl;
	}
	
	std::string refString = str.str();
	
	widgetUIDMap[refString] = w;
	
	return str.str();
}

// This just destroys the reference. Doesn't destroy the Widget.
static void widgetDestroyUID(const std::string &uid) {
	if(widgetUIDMap.count(uid)) {
		widgetUIDMap.erase(uid);
	}
}

// Get a Widget by its UID.
Widget *widgetGetByUID(const std::string &uid) {
	if(widgetUIDMap.count(uid)) {
		return widgetUIDMap[uid];
	}
	return NULL;
}

// --------------------------------------------------------------------
//  Lua wrapper stuff
// --------------------------------------------------------------------

// Wrapper for auto-generated naming silliness to widgetGetByUID().
Widget *getWidgetByUID(const std::string &uid) {
	return widgetGetByUID(uid);
}

// Wrapper for Lua errors.
void autoGenLuaWrapper_Widget_luaError(lua_State *L, const std::string &error) {
	out("error") << error << endl;
}

static std::vector<Widget*> safeDeleteWidgets;

// Call this OUTSIDE of the event handlers.
void widgetCleanupDeletedWidgets(void) {
	for(unsigned int i = 0; i < safeDeleteWidgets.size(); i++) {
		if(safeDeleteWidgets[i]) {
			delete safeDeleteWidgets[i];
		}
	}
	safeDeleteWidgets.clear();
}

void widgetSafeDelete(Widget *w) {
    autoGenLuaWrapper_Widget_safeDelete(w);
}

void autoGenLuaWrapper_Widget_safeDelete(Widget *p) {
	
	// I know this seems hacky, and totally breaks any kind of OOP
	// strictness, but whatever. I can't trust the Lua code to properly
	// clean its crap up when destroying Widgets.
	if(p->parent) {
		
		WidgetMultiParent *multiParent = dynamic_cast<WidgetMultiParent*>(p->parent);
		if(multiParent) {
			multiParent->disconnectSpecificChild(p);
		}
		
		WidgetSingleParent *singleParent = dynamic_cast<WidgetSingleParent*>(p->parent);
		if(singleParent) {
			singleParent->disconnectChild();
		}
		
		if(!singleParent && !multiParent) {
			// Uh oh. Looks like I probably forgot to add a parent type
			// to this function. Now the program will probably crash
			// eventually.
			
			// Going to assert on this because it's a programming error
			// on this side, not necessarily on Lua.
			
			out("error") << p->getName() << " has a parent (" <<
				p->parent->getName() <<
				"), but I don't know what it is, so I can't clean up its pointer to this." << endl;
			
			assert(singleParent || multiParent);
		}
	}

	// Make sure this isn't handling an event at the moment and some
	// misbehaving Lua script is trying to get rid of it.
    if(widgetIsAlreadySafeDeleted(p)) {
        return;
    }
	
	safeDeleteWidgets.push_back(p);
}

bool widgetIsAlreadySafeDeleted(Widget *w) {
	for(unsigned int i = 0; i < safeDeleteWidgets.size(); i++) {
		if(safeDeleteWidgets[i] == w) {
			return true;
		}
	}

    return false;
}

// --------------------------------------------------------------------
//  WidgetEvent utility stuff
// --------------------------------------------------------------------

const char *widgetGetStringForEvent(WidgetEventType eventType) {
#	define EVENT_TYPE(x) \
		case x: \
			return #x; \
	
	// FIXME: Now would be a good time to resort to boost's stupid
	//   preprocessor library so we (maybe) don't have to keep this
	//   synced with the WidgetEventType structure.
	
	switch(eventType) {
		EVENT_TYPE(WIDGETEVENT_MOUSEMOTION)
		EVENT_TYPE(WIDGETEVENT_MOUSEDOWN)
		EVENT_TYPE(WIDGETEVENT_MOUSEUP)
		EVENT_TYPE(WIDGETEVENT_KEYDOWN)
		EVENT_TYPE(WIDGETEVENT_KEYREPEAT)
		EVENT_TYPE(WIDGETEVENT_KEYUP)
		EVENT_TYPE(WIDGETEVENT_FOCUSENTER)
		EVENT_TYPE(WIDGETEVENT_FOCUSLEAVE)
		EVENT_TYPE(WIDGETEVENT_FOCUSNEXT)
		EVENT_TYPE(WIDGETEVENT_FOCUSPREV)
		EVENT_TYPE(WIDGETEVENT_FOCUSACCEPT)
		EVENT_TYPE(WIDGETEVENT_FOCUSACCEPT_BACK)
		EVENT_TYPE(WIDGETEVENT_IDUNNO)
	}
	
	return NULL;
}

// --------------------------------------------------------------------
//  Widget implementation
// --------------------------------------------------------------------

Widget::Widget(void) {
	width = 412;
	height = 412;
	minWidth = 0;
	minHeight = 0;
	
	backgroundTextureIndex = -1;
	bgOffsetX = 0;
	bgOffsetY = 0;
	ninePatch = NULL;

	mouseWasOverMe = false;
	
	sizeDirty = true;
	
	hasFocus = false;
	
	for(int i = 0; i < WIDGET_NUMMOUSEBUTTONS; i++) {
		mouseButtonsDownOnMe[i] = false;
	}
	
	uid = widgetMakeUID(this);
	parent = NULL;
}

Widget::Widget(TiXmlElement *xml) {
	width = 412;
	height = 412;
	minWidth = 0;
	minHeight = 0;
	backgroundTextureIndex = -1;
	bgOffsetX = 0;
	bgOffsetY = 0;
	ninePatch = NULL;
	parent = NULL;
	
	xml->QueryFloatAttribute("width", &width);
	xml->QueryFloatAttribute("height", &height);
	xml->QueryFloatAttribute("minWidth", &minWidth);
	xml->QueryFloatAttribute("minHeight", &minHeight);
	
	// TODO: Remove these. They suck. :V
	xml->QueryFloatAttribute("bgoffsetx", &bgOffsetX);
	xml->QueryFloatAttribute("bgoffsety", &bgOffsetY);
	
	const char *bgName = xml->Attribute("background");
	if(bgName) {
		setBackgroundImage(bgName);
	}
	
	const char *myName = xml->Attribute("name");
	if(myName) {
		name = myName;
	}
	
	mouseWasOverMe = false;
	
	for(int i = 0; i < WIDGET_NUMMOUSEBUTTONS; i++) {
		mouseButtonsDownOnMe[i] = false;
	}

	sizeDirty = true;
	
	hasFocus = false;

	// Lua-related stuff.
	
	// Set up event handlers.	
	const char *tmp;
#	define LUA_EVENT_HANDLER(event, attribute) \
		tmp = xml->Attribute(attribute); \
		if(tmp) { \
			luaEventHandlers[#event] = string(tmp); \
		}
		
	LUA_EVENT_HANDLER(WIDGETEVENT_MOUSEDOWN,        "onMouseDown");
	LUA_EVENT_HANDLER(WIDGETEVENT_MOUSEMOTION,      "onMouseMotion")
	LUA_EVENT_HANDLER(WIDGETEVENT_MOUSEDOWN,        "onMouseDown")
	LUA_EVENT_HANDLER(WIDGETEVENT_MOUSEUP,          "onMouseUp")
	LUA_EVENT_HANDLER(WIDGETEVENT_KEYDOWN,          "onKeyDown")
	LUA_EVENT_HANDLER(WIDGETEVENT_KEYREPEAT,        "onKeyRepeat")
	LUA_EVENT_HANDLER(WIDGETEVENT_KEYUP,            "onKeyUp")
	LUA_EVENT_HANDLER(WIDGETEVENT_FOCUSENTER,       "onFocusEnter")
	LUA_EVENT_HANDLER(WIDGETEVENT_FOCUSLEAVE,       "onFocusLeave")
	
	// Theoretically, the Lua code should be able to decide where the
	// focus goes for these. But they need to return true if they do,
	// otherwise a container will keep poking around for something
	// to do something with focus.
	LUA_EVENT_HANDLER(WIDGETEVENT_FOCUSNEXT,        "onFocusNext")
	LUA_EVENT_HANDLER(WIDGETEVENT_FOCUSPREV,        "onFocusPrevious")
	
	// Maybe these should be combined?
	// Lua code should actually return something for these ones, too.
	LUA_EVENT_HANDLER(WIDGETEVENT_FOCUSACCEPT,      "onFocusAccept")
	LUA_EVENT_HANDLER(WIDGETEVENT_FOCUSACCEPT_BACK, "onFocusAcceptBackwards")
	
	// Pseudo-events. These are the results of interpretations of other
	// events. For example, onMouseEnter and onMouseExit are sent
	// based on mouse motion events.
	
	// And yeah, I use a different naming convention, because these
	// ones only exist as strings, and not in the WidgetEventType enum.
	LUA_EVENT_HANDLER(onMouseEnter,                 "onMouseEnter")
	LUA_EVENT_HANDLER(onMouseExit,                  "onMouseExit")
	
	
	// Set up reference.
	uid = widgetMakeUID(this);
}

Widget::~Widget(void) {
	if(ninePatch) delete ninePatch;
	widgetDestroyUID(uid);
	
	// Make sure root Widget isn't dangling.
	if(widgetRoot == this) {
		widgetRoot = NULL;
	}
	
	// Clean up native event handler pairs.
	for(std::map<std::string, WidgetNativeEventHandler *>::iterator i = nativeEventHandlers.begin(); i != nativeEventHandlers.end(); i++) {
		delete (*i).second;
	}
}

void Widget::setWidth(float newWidth) {
	float myMinWidth = getMinWidth();
	if(newWidth >= myMinWidth) {
		width = newWidth;
	} else {
		width = myMinWidth;
	}
}

void Widget::setHeight(float newHeight) {
	float myMinHeight = getMinHeight();
	if(newHeight >= myMinHeight) {
		height = newHeight;
	} else {
		height = myMinHeight;
	}
}

float Widget::getWidth(void) {
	return width;
}

float Widget::getHeight(void) {
	return height;
}

void Widget::setMinHeight(float minHeight) {
	this->minHeight = minHeight;
	markSizeDirty();
}

void Widget::setMinWidth(float minWidth) {
	this->minWidth = minWidth;
	markSizeDirty();
}

float Widget::getMinHeight(void) {

	// Ninepatch size also counts for minimum size.
	float npMinHeight = 0;
	if(ninePatch) {
		npMinHeight = ninePatch->outerBox.h - ninePatch->innerBox.h;
	}
	
	return npMinHeight > minHeight ? npMinHeight : minHeight;
}

float Widget::getMinWidth(void) {
	
	// Ninepatch size also counts for minimum size.
	float npMinWidth = 0;
	if(ninePatch) {
		npMinWidth = ninePatch->outerBox.w - ninePatch->innerBox.w;
	}
	
	return npMinWidth > minWidth ? npMinWidth : minWidth;
}

void Widget::draw(void) {
	
	glColor4f(1, 1, 1, 1);
	
	float texWidth  = 64;
	float texHeight = 64;
	
	// For non-ninepatch
	float u[2] = { bgOffsetX, bgOffsetX + width };
	float v[2] = { bgOffsetY, bgOffsetY + height };
	
	if(backgroundTextureIndex >= 0) {
		
		TextureManager *manager = TextureManager::get();
		Texture *backgroundImage = manager->getTextureByIndex(backgroundTextureIndex);

        if(backgroundImage) {

            backgroundImage->bindTexture();

            texWidth = backgroundImage->getOrigWidth();
            texHeight = backgroundImage->getOrigHeight();

		} else {

            glBindTexture(GL_TEXTURE_2D, 0);
            glUseProgramObjectARB(0);

        }

		float pow2Width  = backgroundImage ? float(backgroundImage->getAdjustedWidth()) : 64;
		float pow2Height = backgroundImage ? float(backgroundImage->getAdjustedHeight()) : 64;

		// Non-ninepatch.
		u[0] /= pow2Width;
		u[1] /= pow2Width;
		v[0] /= pow2Height;
		v[1] /= pow2Height;

	} else {
		
		// The only thing that the base Widget drawing handles is the
		// background image. If it's not there, then bail out now.
		return;
	}

	// TODO: Debugging only. Remove this.
	/*
	if(needsMouseEvents()) {
		if(mouseWasOverMe) {
			if(mouseButtonsDownOnMe[0]) {
				glColor4f(1, 0, 1, 1);
			} else {
				glColor4f(1, 0, 0, 1);
			}
		} else {
			glColor4f(0, 1, 0, 1);
		}
	} else {
		glColor4f(1, 1, 1, 1);
	}*/
	
	glEnable(GL_TEXTURE_2D);
	glColor4f(1, 1, 1, 1);
	
	glBegin(GL_QUADS);

	if(ninePatch) {
		
		float np_top    = ninePatch->innerBox.y;
		float np_bottom = ninePatch->innerBox.y + ninePatch->innerBox.h;
		float np_left   = ninePatch->innerBox.x;
		float np_right  = ninePatch->innerBox.y + ninePatch->innerBox.w;
		
#		define DRAWQUAD(left, top, right, bottom, uvleft, uvtop, uvright, uvbottom) { \
			glTexCoord2f(uvleft / texWidth, uvtop / texHeight); \
			glVertex2f(left, top); \
			glTexCoord2f(uvright / texWidth, uvtop / texHeight); \
			glVertex2f(right, top); \
			glTexCoord2f(uvright / texWidth, uvbottom / texHeight); \
			glVertex2f(right, bottom); \
			glTexCoord2f(uvleft / texWidth, uvbottom / texHeight); \
			glVertex2f(left, bottom); \
		}
		
		// Non-stretched.
		
		// Top-left
		DRAWQUAD(
			0, 0, np_left, np_top,
			0, 0, np_left, np_top);
		
		// Top-right
		DRAWQUAD(
			width - (texWidth - np_right), 0, width, np_top,
			np_right, 0, texWidth, np_top);

		// Bottom-left
		DRAWQUAD(
			0, height - (texHeight - np_bottom), np_left, height,
			0, np_bottom, np_left, texHeight);

		// Bottom-right
		DRAWQUAD(
			width - (texWidth - np_right), height - (texHeight - np_bottom), width, height,
			np_right, np_bottom, texWidth, texHeight);

		// These parts stretch stuff.
		
		// Left side
		DRAWQUAD(
			0, np_top, np_left, height - (texHeight - np_bottom),
			0, np_top, np_left, np_bottom);

		// Right side.
		DRAWQUAD(
			width - (texWidth - np_right), np_top, width, height - (texHeight - np_bottom),
			np_right, np_top, texWidth, np_bottom);

		// Top
		DRAWQUAD(
			np_left, 0, width - (texWidth - np_right), np_top,
			np_left, 0, np_right, np_top);

		// Bottom
		DRAWQUAD(
			np_left, height - (texHeight - np_bottom), width - (texWidth - np_right), height,
			np_left, np_bottom, np_right, texHeight);

		// Center.
		DRAWQUAD(
			np_left, np_top, width - (texWidth - np_right), height - (texHeight - np_bottom),
			np_left, np_top, np_right, np_bottom);
	} else {
		
		// No ninepatch. Just draw the background image.
		
		// TODO: Should I stretch this?
		
		glTexCoord2f(u[0], v[0]);
		glVertex2f(0, 0);

		glTexCoord2f(u[1], v[0]);
		glVertex2f(width, 0);

		glTexCoord2f(u[1], v[1]);
		glVertex2f(width, height);

		glTexCoord2f(u[0], v[1]);
		glVertex2f(0, height);
	}	
	
	glEnd();
	glColor4f(1, 1, 1, 1);	
}

void Widget::setBackgroundImage(const std::string &fileName) {

	if(ninePatch) {
		delete ninePatch;
		ninePatch = NULL;
		backgroundTextureIndex = -1;
	}
	
	std::string texFileName = fileName;
	
	if(strEndsWith(".ninepatch", fileName)) {
		// Image filename is a ninepatch file. Parse it.
		
		int npLen = 0;
		char *npFileContents = FileSystem::loadFile(fileName, &npLen);
		
		if(!npFileContents) {
			out("error") << "Couldn't load ninepatch info file \"" << fileName << "\"" << endl;
			return;
		}
		
		// Make sure there's a null terminator on this thing.
		char *npContentsFiltered = new char[npLen + 1];
		for(int i = 0; i < npLen; i++) {
			npContentsFiltered[i] = npFileContents[i];
		}
		npContentsFiltered[npLen] = 0;
		
		istringstream inStr(string(npContentsFiltered), istringstream::in);

		// When we get to loading the texture, make sure we load the
		// actual texture image.
		inStr >> texFileName;
		
		WidgetNinePatch *ninePatch = new WidgetNinePatch();
		memset(ninePatch, 0, sizeof(*ninePatch));

		// Bounds for the ninepatch.
		inStr >> ninePatch->innerBox.x;
		inStr >> ninePatch->innerBox.y;
		inStr >> ninePatch->innerBox.w;
		inStr >> ninePatch->innerBox.h;
		
		ninePatch->innerBox.w -= ninePatch->innerBox.x;
		ninePatch->innerBox.h -= ninePatch->innerBox.y;
		
		this->ninePatch = ninePatch;
		
		// Clean up loaded buffers.
		delete[] npFileContents;
		delete[] npContentsFiltered;
		
	}
	
	// Load the image.
	TextureManager *texManager = TextureManager::get();
	backgroundTextureIndex = texManager->getTextureIndexByName(texFileName);
	
	if(ninePatch) {

		// Set up the outer bounds for the ninepatch, taken from the
		// texture dimensions.

		Texture *tex = texManager->getTextureByIndex(backgroundTextureIndex);

        Vector2D texSize = texManager->getTextureSize(tex->getName());

		ninePatch->outerBox.x = 0;
		ninePatch->outerBox.y = 0;
		ninePatch->outerBox.w = texSize.x;
		ninePatch->outerBox.h = texSize.y;
	}
	
	// Ninepatches affect calculated minimum size, so we need to mark
	// us this as dirty.
	markSizeDirty();
}

std::string Widget::getBackgroundImage(void) const {
	TextureManager *manager = TextureManager::get();
	return manager->getTextureNameByIndex(backgroundTextureIndex);	
}

void Widget::setBackgroundOffset(float x, float y) {
	bgOffsetX = x;
	bgOffsetY = y;
}

bool Widget::needsMouseEvents(void) {
	if(mouseWasOverMe) return true;
	for(int i = 0; i < WIDGET_NUMMOUSEBUTTONS; i++) {
		if(mouseButtonsDownOnMe[i]) return true;
	}
	return false;
}

bool Widget::handleEvent(const WidgetEvent *event) {

	// If we've somehow screened out an event so Lua never sees it,
	// like a mousedown event that didn't happen over the widget, set
	// this to true.
	bool skipLua = false;
	
	// Preserve what Lua returned if it was used.
	bool luaRetVal = false;

	// This is all debugging stuff and should probably go away.
	if(event->type == WIDGETEVENT_MOUSEMOTION) {
	
		if(mouseEventTouchesMe(&event->mouse)) {
			
			// Inside our bounding box.

			// TODO: Remove this. Debugging only.
			/*float x = event->mouse.x;
			float y = event->mouse.y;
			glDisable(GL_TEXTURE_2D);
			if(glUseProgramObjectARB) glUseProgramObjectARB(0);
			glColor4f(1, 0, 0, 1);
			glBegin(GL_QUADS);
				glVertex2f(x+debugDrawOffsetX-5, y+debugDrawOffsetY-5);
				glVertex2f(x+debugDrawOffsetX+5, y+debugDrawOffsetY-5);
				glVertex2f(x+debugDrawOffsetX+5, y+debugDrawOffsetY+5);
				glVertex2f(x+debugDrawOffsetX-5, y+debugDrawOffsetY+5);
			glEnd();*/
	
			if(!mouseWasOverMe) {
				
				mouseWasOverMe = true;
				
				// Call the Lua handler for this. (We don't care about
				// the return value for these ones.)
				handleLuaEvent("onMouseEnter", event);
			}
	
		} else {
			
			if(mouseWasOverMe) {
				
				mouseWasOverMe = false;
				
				// Call Lua event.
				handleLuaEvent("onMouseExit", event);
			}
			
		}
	}
	
	// Mouse down events need to actually happen on the widget to
	// register.
	if(event->type == WIDGETEVENT_MOUSEDOWN) {
		
		if(mouseEventTouchesMe(&event->mouse)) {

			if(event->mouse.button < WIDGET_NUMMOUSEBUTTONS) {
				mouseButtonsDownOnMe[event->mouse.button] = true;
			}
			
		} else {
			
			skipLua = true;
			
		}
		
	}
	
	if(event->type == WIDGETEVENT_FOCUSENTER) {
		this->hasFocus = true;
	}
	
	if(event->type == WIDGETEVENT_FOCUSLEAVE) {
		this->hasFocus = false;
	}
	
	// Do any Lua callbacks.
	if(!skipLua) {
		
		// Screen out mousedown events when the mouse wasn't over the
		// Widget. Don't want to bother the Lua scripter with mouse
		// events that they have to check before doing anything with.
		if(event->type != WIDGETEVENT_MOUSEDOWN || mouseWasOverMe) {
		
			const char *eventName = widgetGetStringForEvent(event->type);
			luaRetVal = handleLuaEvent(eventName, event);
		}
	}
	
	// It doesn't actually matter where a mouse button was released.
	// Child classes should pay attention to where the cursor is to
	// make sure it's released on them if they care.
	
	// !!!!!!!!!! Widgets should save this button's state if they want
	// to react on button release, but only after the mouse was down on
	// THAT WIDGET !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	
	// This happens after Lua, so the Lua handler can still query
	// whether or not the mouse was initially down on it.
	if(event->type == WIDGETEVENT_MOUSEUP) {
		if(event->mouse.button < WIDGET_NUMMOUSEBUTTONS) {
			mouseButtonsDownOnMe[event->mouse.button] = false;
		}
	}
	
	return luaRetVal;
}

bool Widget::handleLuaEvent(const std::string &eventName, const WidgetEvent *originalEvent) {

	// So Lua knows what the heck we're calling from and a bunch of
	// event info to pull important data from. Push all this onto a
	// special stack. (We'll be able to query it from Lua code later.)
	WidgetLuaEventInfo info;
	info.eventName = eventName;
	info.event = originalEvent;
	info.widget = this;
	
	widgetLuaEventStack.push_back(&info);

	// TODO: Check the stack for infinite loops!
	//   (Same event + same Widget anywhere in it would be a good
	//   indication of that.)

	const std::string &eventHandlerCode = luaEventHandlers[eventName];
	
	bool luaRetVal = false;
	
	if(eventHandlerCode.size()) {
		
		int stackTop = lua_gettop(widgetLuaState);
		
		luaL_loadstring(widgetLuaState, eventHandlerCode.c_str());
		if(lua_type(widgetLuaState, -1) == LUA_TSTRING) {
			
			// An error happened. Probably a syntax error.
			out("error") << "Lua: " << lua_tostring(widgetLuaState, -1) << endl;
			lua_pop(widgetLuaState, 1);
			
		} else if(lua_type(widgetLuaState, -1) == LUA_TFUNCTION) {
			
			// Call the Lua function.
			int error = lua_pcall(widgetLuaState, 0, 1, 0);
			
			if(error) {
				
				// Woops...
				if(lua_type(widgetLuaState, -1) == LUA_TSTRING) {
					
					out("error") << "Lua: " << lua_tostring(widgetLuaState, -1) << endl;
					lua_pop(widgetLuaState, -1);
				}
			}
		}
		
		// Get Lua return value and return that.
		if(stackTop < lua_gettop(widgetLuaState)) {
			luaRetVal |= lua_toboolean(widgetLuaState, -1);
			
			// TODO: Remove this.
			//if(luaRetVal) {
			//	out("test") << "Lua is eating a(n) " << eventName << " event." << endl;
			//} else {
			//	out("test") << "Lua isn't eating a(n) " << eventName << " event." << endl;
			//}
		}
		
		// Get rid of any extra crap.
		while(stackTop < lua_gettop(widgetLuaState)) {
			lua_pop(widgetLuaState, 1);
		}
	}
	
	WidgetNativeEventHandler *nativeHandler = nativeEventHandlers[eventName];
	if(nativeHandler) {
		luaRetVal |= nativeHandler->func(this, nativeHandler->param);
	}
	
    if(!luaRetVal) {
        vector< boost::function<bool()> > &handlerList = nativeBindEventHandlers[eventName];
        
        for(unsigned int i = 0; i < handlerList.size(); i++) {
            luaRetVal |= handlerList[i]();
            if(luaRetVal) break;
        }
    }
	
	// Pop us off the stack.
	widgetLuaEventStack.erase(widgetLuaEventStack.end() - 1);
	
	return luaRetVal;
}

bool Widget::isSizeDirty(void) {
	return sizeDirty;
}

void Widget::markSizeDirty(void) {
	sizeDirty = true;
}

const std::string &Widget::getUID(void) {
	return uid;
}

void Widget::setLuaEventHandler(const std::string &event, const std::string &luaCode) {
	luaEventHandlers[event] = luaCode;
}

std::string Widget::getLuaEventHandler(const std::string &event) {
	return luaEventHandlers[event];
}

void Widget::setNativeEventHandler(const std::string &event, WidgetCallback func, void *param) {

	clearNativeEventHandler(event);
	
	WidgetNativeEventHandler *newHandler = new WidgetNativeEventHandler();
	newHandler->func = func;
	newHandler->param = param;
	
	nativeEventHandlers[event] = newHandler;
}

void Widget::clearNativeEventHandler(const std::string &event) {
	
	WidgetNativeEventHandler *currentHandler = nativeEventHandlers[event];
	if(currentHandler) delete currentHandler;
	nativeEventHandlers.erase(event);

    nativeBindEventHandlers[event].clear();
}

void Widget::addNativeBindEventHandler(const std::string &event, boost::function<bool()> handler, bool addToFrontOfList) {

    if(addToFrontOfList) {
        vector< boost::function<bool()> > &handlerList = nativeBindEventHandlers[event];
        handlerList.insert(handlerList.begin(), handler);
    } else {
        nativeBindEventHandlers[event].push_back(handler);
    }

}

Widget *Widget::findChildByName(const std::string &name) {
	if(name == this->name) {
		return this;
	}
	return NULL;
}

void Widget::setName(const std::string &newName) {
	this->name = newName;
}

std::string Widget::getName(void) {
	return name;
}

Widget *Widget::getParent(void) const {
	return parent;
}

bool Widget::widgetIsEventuallyMyParent(Widget *w) {
	Widget *checkWidget = parent;

	// Go up the hierarchy until we see w or hit something without a
	// parent.
	while(checkWidget) {
		if(checkWidget == w) return true;
		checkWidget = checkWidget->parent;
	}
	
	return false;
}


std::string Widget::clipboardText;

void Widget::setClipboard(const std::string &clipBoard) {
	// TODO: OS-specific crap, or fall back on storing string.
	clipboardText = clipBoard;
}

std::string Widget::getClipboard(void) {
	// TODO: OS-specific crap, or fall back on stored string.
	return clipboardText;
}

// --------------------------------------------------------------------
//  Clip area stuff - static functions in Widget class.
// --------------------------------------------------------------------

void Widget::pushClipArea(float x, float y, float w, float h) {

	// Testing code I'm leaving around to test broken clipping areas if it
	// ever pops up again.
	
#	ifdef WIDGET_CLIP_AREA_TEST
#		define F 32
		x += (rand() % F) - (F/2);
		y += (rand() % F) - (F/2);
		w += (rand() % F) - (F/2);
		h += (rand() % F) - (F/2);
	
		cout << clippingStack.size() << " " << x << " " << y << " " << w << " " << h << endl;
#	endif

	glDisable(GL_TEXTURE_2D);
	if(glUseProgramObjectARB) glUseProgramObjectARB(0);
	glColor4f(1, 1, 1, 1);
	
	// Turn off the stencil tests
	glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
	
	// Turn on our incrementing mode.
	glStencilOp(GL_INCR, GL_INCR, GL_INCR);

	// Don't draw this to the screen.
	glColorMask(0, 0, 0, 0);

	glBegin(GL_QUADS);
		glVertex2f(x, y);
		glVertex2f(x + w, y);
		glVertex2f(x + w, y + h);
		glVertex2f(x, y + h);
	glEnd();

	Widget::WidgetRect *clipArea = new Widget::WidgetRect();
	clipArea->x = x;
	clipArea->y = y;
	clipArea->w = w;
	clipArea->h = h;
	clippingStack.push_back(clipArea);
	
	// More testing junk.
#	ifdef WIDGET_CLIP_AREA_TEST	
		Widget::WidgetRect *clipArea2 = new Widget::WidgetRect(*clipArea);	
		recordedClippingAreas.push_back(clipArea2);
#	endif
	
	glColorMask(1, 1, 1, 1);

	// Set the stencil test mode.
	
	// Don't write stencil stuff while drawing the UI.
	glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
	
	// Only draw where the stencil matches our known stencil depth. In
	// theory, this could be GL_GEQUAL without any ill effects. 
	glStencilFunc(GL_EQUAL, clippingStack.size(), 0xffffffff);

	// Turn stencils back on.
	glEnable(GL_STENCIL_TEST);
}

void Widget::popClipArea(void) {

	glDisable(GL_TEXTURE_2D);
	if(glUseProgramObjectARB) glUseProgramObjectARB(0);
	glColor4f(1, 1, 1, 1);
	
	// Turn off the stencil tests
	glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
	
	// Turn on our decrementing mode.
	glStencilOp(GL_DECR, GL_DECR, GL_DECR);

	// Don't draw this to the screen.
	glColorMask(0, 0, 0, 0);

	Widget::WidgetRect *clipArea = clippingStack[clippingStack.size() - 1];

	glBegin(GL_QUADS);
		glVertex2f(clipArea->x, clipArea->y);
		glVertex2f(clipArea->x + clipArea->w, clipArea->y);
		glVertex2f(clipArea->x + clipArea->w, clipArea->y + clipArea->h);
		glVertex2f(clipArea->x, clipArea->y + clipArea->h);
	glEnd();
	
	delete clipArea;
	clippingStack.erase(clippingStack.end() - 1);
	
	glColorMask(1, 1, 1, 1);

	// Set the stencil test mode.
	
	// Don't write stencil stuff while drawing the UI.
	glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
	
	// Only draw where the stencil matches our known stencil depth. In
	// theory, this could be GL_GEQUAL without any ill effects. 
	glStencilFunc(GL_EQUAL, clippingStack.size(), 0xffffffff);
	
	// Turn stencils on.
	glEnable(GL_STENCIL_TEST);
}

/*
void Widget::dumpClipAreas(void) {
	return;
	glDisable(GL_TEXTURE_2D);
	for(unsigned int i = 0; i < recordedClippingAreas.size(); i++) {
		
		Widget::WidgetRect *clipArea = recordedClippingAreas[i];

		glBegin(GL_QUADS);
			glVertex2f(clipArea->x, clipArea->y);
			glVertex2f(clipArea->x + clipArea->w, clipArea->y);
			glVertex2f(clipArea->x + clipArea->w, clipArea->y + clipArea->h);
			glVertex2f(clipArea->x, clipArea->y + clipArea->h);
		glEnd();
		
		delete clipArea;
	}
	recordedClippingAreas.clear();
}
*/

//std::vector<Widget::WidgetRect*> Widget::recordedClippingAreas;

std::vector<Widget::WidgetRect*> Widget::clippingStack;

// --------------------------------------------------------------------
//  Utility functions
// --------------------------------------------------------------------

// TODO: Get rid of these.
float debugDrawOffsetX;
float debugDrawOffsetY;

Vector2D widgetProjectOntoZPlane(const Vector2D &pos, const Matrix4x4 &matrix, float zPlane) {
	Vector2D out;
	
	Matrix4x4 matInvert = matrix.inverse();
	
	Vector3D pt1 = matInvert * Vector3D(pos.x, pos.y, 0);
	Vector3D pt2 = matInvert * Vector3D(pos.x, pos.y, 1);

	if(pt1.z == 0) {
		out.x = pt1.x;
		out.y = pt1.y;
		return out;
	}
		
	if(pt2.z == 0 || pt1.z == pt2.z) {
		out.x = pt2.x;
		out.y = pt2.y;
		return out;
	}

	float vecScale = (zPlane - pt1.z) / (pt2.z - pt1.z);
	
	out.x = pt1.x + (pt2.x - pt1.x) * vecScale;
	out.y = pt1.y + (pt2.y - pt1.y) * vecScale;
	
	return out;
}

Vector2D widgetProjectOntoGLZPlane(const Vector2D &in, float zPlane) {
	
	GLdouble modelMatrix[16];
	GLdouble projectionMatrix[16];
	
	// Pull projection and modelview matrix out of OpenGL.
	glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
	glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix);
	
	// Invert the combination of them.
	// FIXME: You can't actually invert a projection matrix. But it may
	//   be relying on a hack in the invert matrix code to succeed
	//   anyway
	Matrix4x4 matModel(modelMatrix);
	Matrix4x4 matProj(projectionMatrix);
	Matrix4x4 mat = (matProj * matModel).inverse();
	
	// Continue to the general version of this.
	return widgetProjectOntoZPlane(in, matProj * matModel, zPlane);
}

Vector2D widgetWindowToViewport(const Vector2D &windowSpaceCoords) {
	
	// Get the GL viewport.
	GLint viewport[4];
	glGetIntegerv(GL_VIEWPORT, viewport);
	
	// FIXME: Is this getting the size, or just the corner coordinates?
	Vector2D viewportSize(viewport[2], viewport[3]);
	
	// Convert.
	Vector2D ret;
	ret.x = (windowSpaceCoords.x - float(viewport[2] / 2)) / float(viewport[2] / 2);
	ret.y = (windowSpaceCoords.y - float(viewport[3] / 2)) / float(viewport[3] / 2);
	ret.y *= -1;
	
	return ret;
}

// Go through and find all templates and placeholders and expand them
// out, loading more files and recursing if needed.
static void patchUpXml(TiXmlElement *xml, std::map<std::string, TiXmlElement*> *allPlaceFillers = NULL) {
	bool skipIterate = false;
	
	for(TiXmlElement *childXml = xml->FirstChildElement(); childXml; childXml = (skipIterate ? childXml : childXml->NextSiblingElement())) {
		// We actually need to restart if we've changed anything.
		if(skipIterate) childXml = xml->FirstChildElement();
		skipIterate = false;
		
		if(string("template") == childXml->Value()) {
			
			// Read placeholders, THEN load the template file of this.
			
			// Copy this, because we'll clobber whatever is in there if
			// it already exists.
			std::map<std::string, TiXmlElement*> myPlaceFillers;
			if(allPlaceFillers) {
				myPlaceFillers.insert(allPlaceFillers->begin(), allPlaceFillers->end());
			}
	
			// Iterate over children (placeholder fillers) and patch up
			// their XML trees.
			for(TiXmlElement *placeFiller = childXml->FirstChildElement(); placeFiller; placeFiller = placeFiller->NextSiblingElement()) {
				patchUpXml(placeFiller, &myPlaceFillers);
			}
			
			// Add them as entries into our list of placefillers. It's
			// here as a separate thing because I didn't want the new
			// filler values interfering with the patching.
			for(TiXmlElement *placeFiller = childXml->FirstChildElement(); placeFiller; placeFiller = placeFiller->NextSiblingElement()) {
				myPlaceFillers[placeFiller->Value()] = placeFiller;
			}
			
			const char *templateFile = childXml->Attribute("file");
			if(templateFile) {
				
				TiXmlDocument *doc = loadXml(templateFile);
				
				if(doc) {
					patchUpXml(doc->FirstChildElement(), &myPlaceFillers);
					xml->ReplaceChild(childXml, *doc->FirstChildElement());
					delete doc;
					
					skipIterate = true;
				} else {
					
					out("error") << "Failed to load template \"" << templateFile << "\"." << endl;
					
					// Failed to load the template, but we need
					// something there anyway, so replace it with a
					// <null/>.
					
					childXml->SetValue("null");
				}
				
			} else {
				out("error") << "No file specified for template." << endl;
			}
			
		} else if(string("placeholder") == childXml->Value()) {
			
			const char *holderName = childXml->Attribute("id");
			if(holderName) {
			
				TiXmlElement *filler = (*allPlaceFillers)[holderName];
				
				if(filler) {
					
					// Found a placeholder. Replace it with one of our fillers.
					TiXmlNode *lastInsertedFiller = NULL;
					for(TiXmlNode *fillerSub = filler->FirstChild(); fillerSub; fillerSub = fillerSub->NextSibling()) {
						lastInsertedFiller = xml->InsertBeforeChild(childXml, *fillerSub);
					}
					xml->RemoveChild(childXml);
					
					// We're going to go ahead and advance to the next
					// element and tell it to skip iterating next time.
					// This way we can continue even if we replaced our
					// actual TiXmlElement with something less useful.
					childXml = lastInsertedFiller->NextSiblingElement();
					skipIterate = true;
					
				} else {
					out("warning") << "Nothing to fill placeholder \"" << holderName << "\"." << endl;
					childXml->SetValue("null");
				}
				
			} else {
				out("error") << "No id for placeholder." << endl;
				childXml->SetValue("null");
			}
			
		} else {
			
			// Just recurse. Nothing to see here.
			patchUpXml(childXml, allPlaceFillers);
		}
	}
	
}

Widget *widgetCreateFromXMLFile(const std::string &fileName) {

	TiXmlDocument *doc = loadXml(fileName);

	if(doc) {
		// Patch it up with templates and placeholders.
		patchUpXml(doc->FirstChildElement());

		// Move on to the more general version.
		Widget *w = widgetCreateFromXMLElement(doc->FirstChildElement());
		
		delete doc;
		return w;
	}
	
	return NULL;
}

Widget *widgetCreateFromXMLElement(TiXmlElement *xml) {
	if(!xml) return NULL;
	
	std::string widgetTypeStr = xml->Value();
	Widget *newWidget = NULL;

	// FIXME: There's probably a better way than just comparing every
	//   string here. Use a std::map or something.
	
#	define WIDGETTYPE(x, y) if(strcasecmp(widgetTypeStr.c_str(), #x) == 0) { newWidget = new y(xml); } else

	WIDGETTYPE(widget,     Widget)
	WIDGETTYPE(text,       WidgetText)
	WIDGETTYPE(container,  WidgetContainer)
	WIDGETTYPE(paddedbox,  WidgetPaddedBox)
	WIDGETTYPE(scrollable, WidgetScrollable)
	
	if(strcasecmp(widgetTypeStr.c_str(), "null") == 0) { newWidget = NULL; } else { 
		// TODO: Fail to load? Return a NULL?
		out("error") << widgetTypeStr << " is not a type of Widget that I recognize. " << endl;
	}
	
	return newWidget;
}

// --------------------------------------------------------------------
//  Base Widget Lua API
// --------------------------------------------------------------------

// Set a given Widget as the root Widget.
int widgetLuaCB_setRoot(lua_State *L) {
	
	if(lua_gettop(L) < 1) {
		out("error") << "Not enough parameters to widgetSetRoot" << endl;
	}

	Widget *w = autoGenLuaWrapper_getWidgetFromTable(L, -1);
	widgetRoot = w;
	
	return 0;
}

// Get the root reference.
int widgetLuaCB_getRoot(lua_State *L) {
	
	if(widgetRoot) {
		
		autoGenLuaWrapper_getTableFromWidget(L, widgetRoot);
		
	} else {
		
		lua_pushnil(L);
	}
	
	return 1;
}

// Event-related callbacks and utils.

// Get the event at the top of the stack.
static const WidgetLuaEventInfo *widgetGetTopEvent(void) {
	if(widgetLuaEventStack.size()) {
		return widgetLuaEventStack[widgetLuaEventStack.size() - 1];
	}
	return NULL;
}

// Get the mouse location from the event.
int widgetLuaCB_getEventMouseLoc(lua_State *L) {
	const WidgetLuaEventInfo *eventInfo = widgetGetTopEvent();

	if(eventInfo && eventInfo->event &&	(
		eventInfo->event->type == WIDGETEVENT_MOUSEMOTION || 
		eventInfo->event->type == WIDGETEVENT_MOUSEUP ||
		eventInfo->event->type == WIDGETEVENT_MOUSEDOWN)) {
			
		lua_pushnumber(L, eventInfo->event->mouse.x);
		lua_pushnumber(L, eventInfo->event->mouse.y);
		
	} else {
		
		out("error") << "Attempted to get mouse event location info for event \"" << eventInfo->eventName << endl;
		
		lua_pushnumber(L, 0);
		lua_pushnumber(L, 0);
	}
	
	return 2;
}

int widgetLuaCB_getEventMouseButton(lua_State *L) {
	const WidgetLuaEventInfo *eventInfo = widgetGetTopEvent();

	if(eventInfo && eventInfo->event &&	(
		eventInfo->event->type == WIDGETEVENT_MOUSEUP ||
		eventInfo->event->type == WIDGETEVENT_MOUSEDOWN)) {
			
		lua_pushnumber(L, eventInfo->event->mouse.button);
		
	} else {
		
		out("error") << "Attempted to get mouse event button info for event \"" << eventInfo->eventName << endl;
		
		lua_pushnumber(L, 0);
	}
	
	return 1;
}

const WidgetEvent *widgetGetEvent(void) {
	const WidgetLuaEventInfo *eventInfo = widgetGetTopEvent();
	return eventInfo ? eventInfo->event : NULL;
}

int widgetLuaCB_getEventKey(lua_State *L) {
	
	const WidgetLuaEventInfo *eventInfo = widgetGetTopEvent();

	if(eventInfo && eventInfo->event &&	(
		eventInfo->event->type == WIDGETEVENT_KEYDOWN ||
		eventInfo->event->type == WIDGETEVENT_KEYREPEAT ||
		eventInfo->event->type == WIDGETEVENT_KEYUP)) {
		
		char filteredChar = eventInfo->event->keyboard.convertedKey;
		if(filteredChar == '\r') filteredChar = '\n';
		
		char str[2] = {filteredChar, 0};

		lua_pushstring(L, str);
		
	} else {
		
		out("error") << "Attempted to get keyboard event info for event \"" << eventInfo->eventName << endl;
		
		lua_pushstring(L, "");
		
	}
	
	return 1;
}

int widgetLuaCB_getEventWidget(lua_State *L) {
	const WidgetLuaEventInfo *eventInfo = widgetGetTopEvent();
	
	autoGenLuaWrapper_getTableFromWidget(L, eventInfo->widget);
	return 1;
}

// We need this to have it actually realize things are even focused
// when initially set to root.
int widgetLuaCB_focusRoot(lua_State *L) {
	
	if(widgetRoot) {
		WidgetEvent event;
		event.type = WIDGETEVENT_FOCUSNEXT;
		widgetRoot->handleEvent(&event);
	}

	return 0;
}

// TODO: Keyboard event information.

void widgetInitLua(lua_State *L) {
	widgetLuaState = L;
	
	autoGenLuaWrapper_Widget_init(L);
	
	// TODO: These should probably all go on the main Widget class as
	//   static thingers.
	lua_register(L, "widgetSetRoot", widgetLuaCB_setRoot);
	lua_register(L, "widgetGetRoot", widgetLuaCB_getRoot);
	lua_register(L, "widgetGetEventMouseLoc", widgetLuaCB_getEventMouseLoc);
	lua_register(L, "widgetGetEventMouseButton", widgetLuaCB_getEventMouseButton);
	lua_register(L, "widgetGetEventWidget", widgetLuaCB_getEventWidget);
	lua_register(L, "widgetGetEventKey", widgetLuaCB_getEventKey);
	lua_register(L, "widgetFocusRoot", widgetLuaCB_focusRoot);
}

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

static vector<KeyRepeatRecord*> repeatKeys;

static 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);
}

static 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;
		}
	}
}

// FIXME: Should probably find a better way to do this...
//   (Works as long as there's only one keyboard? :V)
static bool shiftHeld = false;
static bool ctrlHeld  = false;
static bool altHeld   = false;

void widgetConvertSDLEvent(const SDL_Event *sdlEvent, WidgetEvent *widgetEvent) {
	
	
	memset(widgetEvent, 0, sizeof(WidgetEvent));
	
	Vector2D convertedCoords1;
	Vector2D convertedCoords2;

	if(sdlEvent->type == SDL_KEYDOWN && sdlEvent->key.keysym.sym == SDLK_TAB) {
		
		widgetEvent->type = shiftHeld ? WIDGETEVENT_FOCUSPREV : WIDGETEVENT_FOCUSNEXT;
		return;
		
	}
	
	switch(sdlEvent->type) {
		
		case SDL_KEYDOWN:
		case SDL_KEYUP:
			{
				bool shiftSwitch = sdlEvent->type == SDL_KEYDOWN;
				if(sdlEvent->key.keysym.sym == SDLK_LSHIFT || sdlEvent->key.keysym.sym == SDLK_RSHIFT) {
					shiftHeld = shiftSwitch;
				}
				if(sdlEvent->key.keysym.sym == SDLK_LCTRL  || sdlEvent->key.keysym.sym == SDLK_RCTRL) {
					ctrlHeld = shiftSwitch;
				}
				if(sdlEvent->key.keysym.sym == SDLK_LALT  || sdlEvent->key.keysym.sym == SDLK_RALT) {
					altHeld = shiftSwitch;
				}
			}
			
			// Hello, this is why switch() was invented.
			// TODO: Fix this shit.
			switch(sdlEvent->key.keysym.sym) {
				case SDLK_LEFT:     widgetEvent->keyboard.keyCode = WIDGET_KEY_LEFT;  break;
				case SDLK_RIGHT:    widgetEvent->keyboard.keyCode = WIDGET_KEY_RIGHT; break;
				case SDLK_UP:       widgetEvent->keyboard.keyCode = WIDGET_KEY_UP;    break;
				case SDLK_DOWN:     widgetEvent->keyboard.keyCode = WIDGET_KEY_DOWN;  break;
				case SDLK_HOME:     widgetEvent->keyboard.keyCode = WIDGET_KEY_HOME;  break;
				case SDLK_END:      widgetEvent->keyboard.keyCode = WIDGET_KEY_END;   break;
				case SDLK_PAGEDOWN: widgetEvent->keyboard.keyCode = WIDGET_KEY_PAGEDOWN; break;
				case SDLK_PAGEUP:   widgetEvent->keyboard.keyCode = WIDGET_KEY_PAGEUP; break;
                case SDLK_ESCAPE:   widgetEvent->keyboard.keyCode = WIDGET_KEY_ESCAPE; break;

				case SDLK_RETURN:
					widgetEvent->keyboard.keyCode = WIDGET_KEY_RETURN;
					widgetEvent->keyboard.convertedKey = sdlEvent->key.keysym.unicode & 0xff;
					break;
				
				case SDLK_SPACE:
					widgetEvent->keyboard.keyCode = WIDGET_KEY_SPACE;
					widgetEvent->keyboard.convertedKey = sdlEvent->key.keysym.unicode & 0xff;
					break;
				
				default:
					if(!(sdlEvent->key.keysym.unicode & 0xff00)) {
						widgetEvent->keyboard.convertedKey = sdlEvent->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);

			if(sdlEvent->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;
			
			break;
			
		case SDL_MOUSEMOTION:
			widgetEvent->type = WIDGETEVENT_MOUSEMOTION;
			
			convertedCoords1 = widgetWindowToViewport(Vector2D(sdlEvent->motion.x, sdlEvent->motion.y));
			
			convertedCoords2 = widgetProjectOntoGLZPlane(convertedCoords1);
			widgetEvent->mouse.x = convertedCoords2.x;
			widgetEvent->mouse.y = convertedCoords2.y;
			
			break;

		case SDL_MOUSEBUTTONDOWN:
		case SDL_MOUSEBUTTONUP:
			widgetEvent->type = (sdlEvent->type == SDL_MOUSEBUTTONDOWN ? WIDGETEVENT_MOUSEDOWN : WIDGETEVENT_MOUSEUP);
			
			convertedCoords1 = widgetWindowToViewport(Vector2D(sdlEvent->button.x, sdlEvent->button.y));
			
			convertedCoords2 = widgetProjectOntoGLZPlane(convertedCoords1);
			widgetEvent->mouse.x = convertedCoords2.x;
			widgetEvent->mouse.y = convertedCoords2.y;
			widgetEvent->mouse.button = sdlEvent->button.button - 1;
			
			break;
			
		default:
			break;
	}
}

void widgetHandleKeyRepeats(Widget *w) {
	// 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;
			w->handleEvent(&widgetEvent);
		} else {
			repeatKeys[i]->timeout--;
		}
	}
}

static GLdouble modelMatrix[16];
static GLdouble projectionMatrix[16];
	
void widgetPushTransforms(float screenWidth, float screenHeight) {

	// Pull projection and modelview matrix out of OpenGL.
	glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
	glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix);
	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluOrtho2D(0, screenWidth, screenHeight, 0);
}

void widgetPopTransforms(void) {
	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glMultMatrixd(projectionMatrix);
	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glMultMatrixd(modelMatrix);
}


static map<std::string, std::string> widgetDefaultStrValues;

void widgetSetDefaultStr(const std::string &name, const std::string &value) {
	widgetDefaultStrValues[name] = value;
}

std::string widgetGetDefaultStr(const std::string &name) {
	map<std::string, std::string>::iterator i = widgetDefaultStrValues.find(name);
	
	if(i != widgetDefaultStrValues.end()) {
		return (*i).second;
	}
	
	return "";
}


static vector<unsigned int*> displayListSentinels;

void widgetAddDisplayListSentinel(unsigned int *l) {
	displayListSentinels.push_back(l);
}

void widgetRemoveDisplayListSentinel(unsigned int *l) {
	for(unsigned int i = 0; i < displayListSentinels.size(); i++) {
		if(displayListSentinels[i] == l) {
			displayListSentinels[i] = displayListSentinels[displayListSentinels.size() - 1];
			displayListSentinels.erase(displayListSentinels.end() - 1);
		}
	}
}

void widgetInvalidateDisplayLists(void) {
	for(unsigned int i = 0; i < displayListSentinels.size(); i++) {
		
		// FIXME: Is this needed?
		if(*(displayListSentinels[i])) {
			glDeleteLists(*(displayListSentinels[i]), 1);
		}
		
		*(displayListSentinels[i]) = 0;
	}
}
