/**
* Copyright (c) 2006-2012 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty.  In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
*    claim that you wrote the original software. If you use this software
*    in a product, an acknowledgment in the product documentation would be
*    appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
*    misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/

#include "Event.h"

using love::thread::Mutex;
using love::thread::Lock;

namespace love
{
namespace event
{
	Message::Message(std::string name, Variant *a, Variant *b, Variant *c, Variant *d)
		: name(name), nargs(0)
	{
		args[0] = a;
		args[1] = b;
		args[2] = c;
		args[3] = d;
		for (int i = 0; i < 4; i++)
		{
			if (!args[i])
				continue;
			args[i]->retain();
			nargs++;
		}
	}

	Message::~Message()
	{
		for (int i = 0; i < nargs; i++)
			args[i]->release();
	}

	int Message::toLua(lua_State *L)
	{
		luax_pushstring(L, name);
		for (int i = 0; i < nargs; i++)
			args[i]->toLua(L);
		return nargs+1;
	}

	Message *Message::fromLua(lua_State *L, int n)
	{
		std::string name = luax_checkstring(L, n);
		n++;
		Message *m = new Message(name);
		for (int i = 0; i < 4; i++)
		{
			if (lua_isnoneornil(L, n+i))
				break;
			m->args[i] = Variant::fromLua(L, n+i);
			if (!m->args[i])
			{
				delete m;
				luaL_error(L, "Argument %d can't be stored safely\nExpected boolean, number, string or userdata.", n+i);
				return NULL;
			}
			m->nargs++;
		}
		return m;
	}

	Event::~Event()
	{
	}

	void Event::push(Message *msg)
	{
		Lock lock(mutex);
		msg->retain();
		queue.push(msg);
	}

	bool Event::poll(Message *&msg)
	{
		Lock lock(mutex);
		if (queue.empty())
			return false;
		msg = queue.front();
		queue.pop();
		return true;
	}

	void Event::clear()
	{
		Lock lock(mutex);
		while (!queue.empty())
		{
			queue.back()->release();
			queue.pop();
		}
	}

	bool Event::getConstant(const char * in, love::mouse::Mouse::Button & out)
	{
		return buttons.find(in, out);
	}

	bool Event::getConstant(love::mouse::Mouse::Button in, const char *& out)
	{
		return buttons.find(in, out);
	}

	bool Event::getConstant(const char * in, love::keyboard::Keyboard::Key & out)
	{
		return keys.find(in, out);
	}

	bool Event::getConstant(love::keyboard::Keyboard::Key in, const char *& out)
	{
		return keys.find(in, out);
	}

	StringMap<love::mouse::Mouse::Button, love::mouse::Mouse::BUTTON_MAX_ENUM>::Entry Event::buttonEntries[] =
	{
		{"l", love::mouse::Mouse::BUTTON_LEFT},
		{"m", love::mouse::Mouse::BUTTON_MIDDLE},
		{"r", love::mouse::Mouse::BUTTON_RIGHT},
		{"wu", love::mouse::Mouse::BUTTON_WHEELUP},
		{"wd", love::mouse::Mouse::BUTTON_WHEELDOWN},
		{"x1", love::mouse::Mouse::BUTTON_X1},
		{"x2", love::mouse::Mouse::BUTTON_X2},
	};

	StringMap<love::mouse::Mouse::Button, love::mouse::Mouse::BUTTON_MAX_ENUM> Event::buttons(Event::buttonEntries, sizeof(Event::buttonEntries));

	StringMap<love::keyboard::Keyboard::Key, love::keyboard::Keyboard::KEY_MAX_ENUM>::Entry Event::keyEntries[] =
	{
		{"backspace", love::keyboard::Keyboard::KEY_BACKSPACE},
		{"tab", love::keyboard::Keyboard::KEY_TAB},
		{"clear", love::keyboard::Keyboard::KEY_CLEAR},
		{"return", love::keyboard::Keyboard::KEY_RETURN},
		{"pause", love::keyboard::Keyboard::KEY_PAUSE},
		{"escape", love::keyboard::Keyboard::KEY_ESCAPE},
		{" ", love::keyboard::Keyboard::KEY_SPACE},
		{"!", love::keyboard::Keyboard::KEY_EXCLAIM},
		{"\"", love::keyboard::Keyboard::KEY_QUOTEDBL},
		{"#", love::keyboard::Keyboard::KEY_HASH},
		{"$", love::keyboard::Keyboard::KEY_DOLLAR},
		{"&", love::keyboard::Keyboard::KEY_AMPERSAND},
		{"'", love::keyboard::Keyboard::KEY_QUOTE},
		{"(", love::keyboard::Keyboard::KEY_LEFTPAREN},
		{")", love::keyboard::Keyboard::KEY_RIGHTPAREN},
		{"*", love::keyboard::Keyboard::KEY_ASTERISK},
		{"+", love::keyboard::Keyboard::KEY_PLUS},
		{",", love::keyboard::Keyboard::KEY_COMMA},
		{"-", love::keyboard::Keyboard::KEY_MINUS},
		{".", love::keyboard::Keyboard::KEY_PERIOD},
		{"/", love::keyboard::Keyboard::KEY_SLASH},
		{"0", love::keyboard::Keyboard::KEY_0},
		{"1", love::keyboard::Keyboard::KEY_1},
		{"2", love::keyboard::Keyboard::KEY_2},
		{"3", love::keyboard::Keyboard::KEY_3},
		{"4", love::keyboard::Keyboard::KEY_4},
		{"5", love::keyboard::Keyboard::KEY_5},
		{"6", love::keyboard::Keyboard::KEY_6},
		{"7", love::keyboard::Keyboard::KEY_7},
		{"8", love::keyboard::Keyboard::KEY_8},
		{"9", love::keyboard::Keyboard::KEY_9},
		{":", love::keyboard::Keyboard::KEY_COLON},
		{";", love::keyboard::Keyboard::KEY_SEMICOLON},
		{"<", love::keyboard::Keyboard::KEY_LESS},
		{"=", love::keyboard::Keyboard::KEY_EQUALS},
		{">", love::keyboard::Keyboard::KEY_GREATER},
		{"?", love::keyboard::Keyboard::KEY_QUESTION},
		{"@", love::keyboard::Keyboard::KEY_AT},

		{"[", love::keyboard::Keyboard::KEY_LEFTBRACKET},
		{"\\", love::keyboard::Keyboard::KEY_BACKSLASH},
		{"]", love::keyboard::Keyboard::KEY_RIGHTBRACKET},
		{"^", love::keyboard::Keyboard::KEY_CARET},
		{"_", love::keyboard::Keyboard::KEY_UNDERSCORE},
		{"`", love::keyboard::Keyboard::KEY_BACKQUOTE},
		{"a", love::keyboard::Keyboard::KEY_A},
		{"b", love::keyboard::Keyboard::KEY_B},
		{"c", love::keyboard::Keyboard::KEY_C},
		{"d", love::keyboard::Keyboard::KEY_D},
		{"e", love::keyboard::Keyboard::KEY_E},
		{"f", love::keyboard::Keyboard::KEY_F},
		{"g", love::keyboard::Keyboard::KEY_G},
		{"h", love::keyboard::Keyboard::KEY_H},
		{"i", love::keyboard::Keyboard::KEY_I},
		{"j", love::keyboard::Keyboard::KEY_J},
		{"k", love::keyboard::Keyboard::KEY_K},
		{"l", love::keyboard::Keyboard::KEY_L},
		{"m", love::keyboard::Keyboard::KEY_M},
		{"n", love::keyboard::Keyboard::KEY_N},
		{"o", love::keyboard::Keyboard::KEY_O},
		{"p", love::keyboard::Keyboard::KEY_P},
		{"q", love::keyboard::Keyboard::KEY_Q},
		{"r", love::keyboard::Keyboard::KEY_R},
		{"s", love::keyboard::Keyboard::KEY_S},
		{"t", love::keyboard::Keyboard::KEY_T},
		{"u", love::keyboard::Keyboard::KEY_U},
		{"v", love::keyboard::Keyboard::KEY_V},
		{"w", love::keyboard::Keyboard::KEY_W},
		{"x", love::keyboard::Keyboard::KEY_X},
		{"y", love::keyboard::Keyboard::KEY_Y},
		{"z", love::keyboard::Keyboard::KEY_Z},
		{"delete", love::keyboard::Keyboard::KEY_DELETE},

		{"kp0", love::keyboard::Keyboard::KEY_KP0},
		{"kp1", love::keyboard::Keyboard::KEY_KP1},
		{"kp2", love::keyboard::Keyboard::KEY_KP2},
		{"kp3", love::keyboard::Keyboard::KEY_KP3},
		{"kp4", love::keyboard::Keyboard::KEY_KP4},
		{"kp5", love::keyboard::Keyboard::KEY_KP5},
		{"kp6", love::keyboard::Keyboard::KEY_KP6},
		{"kp7", love::keyboard::Keyboard::KEY_KP7},
		{"kp8", love::keyboard::Keyboard::KEY_KP8},
		{"kp9", love::keyboard::Keyboard::KEY_KP9},
		{"kp.", love::keyboard::Keyboard::KEY_KP_PERIOD},
		{"kp/", love::keyboard::Keyboard::KEY_KP_DIVIDE},
		{"kp*", love::keyboard::Keyboard::KEY_KP_MULTIPLY},
		{"kp-", love::keyboard::Keyboard::KEY_KP_MINUS},
		{"kp+", love::keyboard::Keyboard::KEY_KP_PLUS},
		{"kpenter", love::keyboard::Keyboard::KEY_KP_ENTER},
		{"kp=", love::keyboard::Keyboard::KEY_KP_EQUALS},

		{"up", love::keyboard::Keyboard::KEY_UP},
		{"down", love::keyboard::Keyboard::KEY_DOWN},
		{"right", love::keyboard::Keyboard::KEY_RIGHT},
		{"left", love::keyboard::Keyboard::KEY_LEFT},
		{"insert", love::keyboard::Keyboard::KEY_INSERT},
		{"home", love::keyboard::Keyboard::KEY_HOME},
		{"end", love::keyboard::Keyboard::KEY_END},
		{"pageup", love::keyboard::Keyboard::KEY_PAGEUP},
		{"pagedown", love::keyboard::Keyboard::KEY_PAGEDOWN},

		{"f1", love::keyboard::Keyboard::KEY_F1},
		{"f2", love::keyboard::Keyboard::KEY_F2},
		{"f3", love::keyboard::Keyboard::KEY_F3},
		{"f4", love::keyboard::Keyboard::KEY_F4},
		{"f5", love::keyboard::Keyboard::KEY_F5},
		{"f6", love::keyboard::Keyboard::KEY_F6},
		{"f7", love::keyboard::Keyboard::KEY_F7},
		{"f8", love::keyboard::Keyboard::KEY_F8},
		{"f9", love::keyboard::Keyboard::KEY_F9},
		{"f10", love::keyboard::Keyboard::KEY_F10},
		{"f11", love::keyboard::Keyboard::KEY_F11},
		{"f12", love::keyboard::Keyboard::KEY_F12},
		{"f13", love::keyboard::Keyboard::KEY_F13},
		{"f14", love::keyboard::Keyboard::KEY_F14},
		{"f15", love::keyboard::Keyboard::KEY_F15},

		{"numlock", love::keyboard::Keyboard::KEY_NUMLOCK},
		{"capslock", love::keyboard::Keyboard::KEY_CAPSLOCK},
		{"scrollock", love::keyboard::Keyboard::KEY_SCROLLOCK},
		{"rshift", love::keyboard::Keyboard::KEY_RSHIFT},
		{"lshift", love::keyboard::Keyboard::KEY_LSHIFT},
		{"rctrl", love::keyboard::Keyboard::KEY_RCTRL},
		{"lctrl", love::keyboard::Keyboard::KEY_LCTRL},
		{"ralt", love::keyboard::Keyboard::KEY_RALT},
		{"lalt", love::keyboard::Keyboard::KEY_LALT},
		{"rmeta", love::keyboard::Keyboard::KEY_RMETA},
		{"lmeta", love::keyboard::Keyboard::KEY_LMETA},
		{"lsuper", love::keyboard::Keyboard::KEY_LSUPER},
		{"rsuper", love::keyboard::Keyboard::KEY_RSUPER},
		{"mode", love::keyboard::Keyboard::KEY_MODE},
		{"compose", love::keyboard::Keyboard::KEY_COMPOSE},

		{"help", love::keyboard::Keyboard::KEY_HELP},
		{"print", love::keyboard::Keyboard::KEY_PRINT},
		{"sysreq", love::keyboard::Keyboard::KEY_SYSREQ},
		{"break", love::keyboard::Keyboard::KEY_BREAK},
		{"menu", love::keyboard::Keyboard::KEY_MENU},
		{"power", love::keyboard::Keyboard::KEY_POWER},
		{"euro", love::keyboard::Keyboard::KEY_EURO},
		{"undo", love::keyboard::Keyboard::KEY_UNDO},

		{"unknown", love::keyboard::Keyboard::KEY_UNKNOWN},
	};

	StringMap<love::keyboard::Keyboard::Key, love::keyboard::Keyboard::KEY_MAX_ENUM> Event::keys(Event::keyEntries, sizeof(Event::keyEntries));

} // event
} // love
