#include "ControllerBase.h"

#include "ModelBase.h"
#include "ViewBase.h"
#include "CommandsGlobal.h"

#include "keytable.h"

namespace Engine {

static ControllerBase *ControllerInstance = NULL;

/*
 * Helper functions
 */

/** \brief Converts the SDL_BUTTON_* to a value we know */
MouseButton convert_sdl_button (Uint8 button) {
	MouseButton mouse_button;
	switch (button) {
		case SDL_BUTTON_LEFT:
			mouse_button = MouseButtonLeft;
			break;
		case SDL_BUTTON_MIDDLE:
			mouse_button = MouseButtonMiddle;
			break;
		case SDL_BUTTON_RIGHT:
			mouse_button = MouseButtonRight;
			break;
		case SDL_BUTTON_WHEELUP:
			mouse_button = MouseButtonWheelUp;
			break;
		case SDL_BUTTON_WHEELDOWN:
			mouse_button = MouseButtonWheelDown;
			break;
		default:
			mouse_button = MouseButtonUnknown;
			break;
	}

	return mouse_button;
}

/*
 * Inherited Module functions
 */
int ControllerBase::OnInit (int argc, char* argv[]) {
	LogDebug ("Controller Init");

	// clear all bindings
	int i;
	for (i = 0; i < BINDING_KEYS_LAST; i++) {
		mBindings[i] = "";
		mButtonStates.set(i, false);
	}

	uistate.activeitem = 0;
	uistate.hotitem = 0;

	uistate.kbditem = 0;
	uistate.last_keysym = SDLK_CLEAR;
	uistate.last_unicode = 0;

	uistate.lastwidget = 0;

	ControllerInstance = this;

	return 0;
}

void ControllerBase::OnDestroy () {
	ControllerInstance = NULL;

	LogDebug ("Controller Destroy");
}

/*
 * Module specific functions 
 */
bool ControllerBase::BindKey (int key, const char *command) {
	if (key <= 0 || key >= BINDING_KEYS_LAST) {
		LogError ("Could not bind to key with index '%d': invalid index!", key);
		return false;
	}

	mBindings[key] = command;

	return true;
}

void ControllerBase::Process () {
	ProcessEvents ();

	mView->CalcWorldCoordinates (mMouseScreenPosition[0], mMouseScreenPosition[1],
			0., mMouseWorldPosition);

	/*
	LogMessage ("Screenpos = %2d,%2d Worldpos = %f,%f,%f",
			mMouseScreenPosition[0], mMouseScreenPosition[1],
			mMouseWorldPosition[0], mMouseWorldPosition[1], mMouseWorldPosition[2]);
	*/
}

void ControllerBase::ProcessEvents () {
	SDL_Event event;

	while (SDL_PollEvent(&event)) {
		/* We are only worried about SDL_KEYDOWN and SDL_KEYUP events */
		switch (event.type) {
		case SDL_KEYDOWN:
			OnKeyDown (event.key.keysym);
			break;

		case SDL_KEYUP:
			OnKeyUp (event.key.keysym);
			break;

		case SDL_MOUSEMOTION:
			OnMouseMotion(event.motion.x, event.motion.y);
			break;

		case SDL_MOUSEBUTTONDOWN:
			OnMouseButtonDown (event.button.button, event.button.x, event.button.y);
			break;

		case SDL_MOUSEBUTTONUP:
			OnMouseButtonUp (event.button.button, event.button.x, event.button.y);
			break;

		case SDL_VIDEORESIZE:
			OnVideoResize (event.resize.w, event.resize.h);
			break;
		
		case SDL_QUIT:
			EngineSetStatus (EngineStatusStopping);
			break;

		default:
			break;
		}
	}
}

/** \brief Activates or deactivates unicode processing and key delays of the keyboard inputs */
void ControllerBase::EnableTextinput (bool textinput_state) {
	if (textinput_state) {
		SDL_EnableUNICODE(1);
		SDL_EnableKeyRepeat(500, 50);
	} else {
		SDL_EnableUNICODE(-1);
		SDL_EnableKeyRepeat(0, 100);
	}
}

void ControllerBase::IMGUIFinish () {
	if (GetButtonState(MouseButtonLeft) == false) {
		uistate.activeitem = 0;
	} else {
		if (uistate.activeitem == 0)
			uistate.activeitem = -1;
	}

	// also reset the last keysym such that old values will not be reported
	uistate.last_keysym = SDLK_FIRST;
}

/** \brief Keyboard processing */
bool ControllerBase::OnKeyDown (const SDL_keysym &keysym) {
	mButtonStates.set(keysym.sym, true);
	uistate.last_keysym = keysym.sym;

	// Only when Unicode processing is activated store the unicode value
	if (SDL_EnableUNICODE(-1)) {
		uistate.last_unicode = keysym.unicode;
	}

	if (mView->mOverlayManager.SendKeyDown (keysym))
		return true;

	if (mBindings[keysym.sym].size () != 0) {
		QueueCommand (mBindings[keysym.sym]);
		return true;
	}

	return false;
}

/** \brief Keyboard processing */
bool ControllerBase::OnKeyUp (const SDL_keysym &keysym) {
	mButtonStates.set(keysym.sym, false);

	if (mView->mOverlayManager.SendKeyUp (keysym))
		return true;

	if (mBindings[keysym.sym].size () != 0) {
		if (mBindings[keysym.sym][0] == '+') {
			std::string upcommand = mBindings[keysym.sym];
			upcommand[0] = '-';
			QueueCommand (upcommand);
			return true;
		}
	}

	return false;
}

/** \brief Mouse processing */
bool ControllerBase::OnMouseButtonDown (Uint8 button, Uint16 xpos, Uint16 ypos) {
	MouseButton mouse_button = convert_sdl_button (button);
	mButtonStates.set(mouse_button, true);

	if (mView->mOverlayManager.SendMouseButtonDown (button, xpos, ypos))
		return true;

	if (mBindings[mouse_button].size () != 0) {
		QueueCommand (mBindings[mouse_button]);
		return true;
	}
	
	return false;
}

/** \brief Mouse processing */
bool ControllerBase::OnMouseButtonUp (Uint8 button, Uint16 xpos, Uint16 ypos) {
	MouseButton mouse_button = convert_sdl_button (button);
	mButtonStates.set(mouse_button, false);

	if (mView->mOverlayManager.SendMouseButtonUp (button, xpos, ypos))
		return true;

	if (mBindings[mouse_button].size () != 0) {
		if (mBindings[mouse_button][0] == '+') {
			std::string upcommand = mBindings[mouse_button];
			upcommand[0] = '-';
			QueueCommand (upcommand);
			return true;
		}
	}

	return false;
}

/** \brief Mouse processing */
bool ControllerBase::OnMouseMotion (const int xnew, const int ynew) {
	mMouseScreenPosition[0] = xnew;
	mMouseScreenPosition[1] = ynew;
	return false;
}

/** \brief Video */
bool ControllerBase::OnVideoResize (int width, int height) {
	mView->Resize (width, height);

	return true;
}

int convert_keystring (const char *key_val) {
	std::string keystr (key_val);
	
	// convert the keystr to lowercase
	for (std::string::iterator iter = keystr.begin(); iter != keystr.end(); iter++)
		(*iter) = tolower(*iter);

	// now we search through the table for he value we were given
	int i = 0;
	while (key_table[i].keynum != keytable_last) {
		if (keystr.compare(key_table[i].keystr) == 0)
			return key_table[i].keynum;

		i++;
	}

	return 0;
}

const char* convert_keycode (const int &keycode) {
	int i = 0;
	while (key_table[i].keynum != keytable_last) {
		if (key_table[i].keynum == keycode)
			return key_table[i].keystr;

		i++;
	}

	return NULL;
}

/*
 * Commands for the ControllerBase
 */
bool Cmd_Bind (const std::vector<std::string> args) {
	if (ControllerInstance == NULL) {
		CommandSetErrorString("Could not bind key: Controller not yet initialized!");
		return false;
	}

	if (args.size() != 2) {
		CommandSetErrorString("usage: bind <key> <command>");
		return false;
	}

	int key = convert_keystring (args[0].c_str());

	if (key == 0) {
		std::ostringstream error_msg;
		error_msg << "bind failed: invalid key '" << args[0] << '"';
		CommandSetErrorString(error_msg.str());
		return false;
	}

	if (!ControllerInstance->BindKey (key, args[1].c_str()))
		return false;

	return true;
}

void ControllerBase::OnRegisterCommands () {
	AddCommand ("bind", Cmd_Bind);
}

}

