#include "IMGUIControls.h"

#include "Engine.h"
#include "ControllerBase.h"
#include "ViewBase.h"
#include <GL/gl.h>
#include "DrawingsGL.h"
#include "keytable.h"

Engine::ControllerBase *controller = NULL;
Engine::ViewBase *view = NULL;

namespace Engine {
namespace GUI {

/** \brief Checks whether the mouse is in the given rectangle */
bool regionhit (int x, int y, int w, int h) {
	controller = EngineGetController();
	assert (controller);

	int mouse_pos[2];

	controller->GetMouseScreenPosition(mouse_pos);

	if (mouse_pos[0] < x ||
			mouse_pos[1] < y ||
			mouse_pos[0] >= x + w ||
			mouse_pos[1] >= y + h)
		return false;
	return true;
}

void DrawBlock (int x, int y, int w, int h) {
	const float shading_dark = 0.5;
	const float shading_light = 1.3;

	float color[4];
	glGetFloatv (GL_CURRENT_COLOR, color);

	glBegin(GL_QUADS);

 	// middle part
	glVertex3f (x, y, 0.);
	glVertex3f (x, y + h, 0.);
	glVertex3f (x + w, y + h, 0.);
	glVertex3f (x + w, y, 0.);

	glEnd();

	glColor4fv (color);

	// "Shading"
	glLineWidth(3);
	glColor3f (color[0] * shading_dark, color[1] * shading_dark, color[2] * shading_dark);

	glBegin(GL_LINE_STRIP);
	glVertex3f (x,y + 2, 0.);
	glVertex3f (x,y +h, 0.);
	glVertex3f (x + w - 2, y + h, 0.);
	glEnd();
}

void DrawRoundedBlock (int x, int y, int w, int h) {
	const int d = 16;
	const float shading_dark = 0.5;
	const float shading_light = 1.3;

	assert (h > d);
	assert (w > d);

	glBegin(GL_QUADS);
	// lower part
	glVertex3f (x, y, 0.);
	glVertex3f (x, y + d, 0.);
	glVertex3f (x + w, y + d, 0.);
	glVertex3f (x + w - d, y, 0.);

 	// middle part
	glVertex3f (x, y + d, 0.);
	glVertex3f (x, y + d + h - 2 * d, 0.);
	glVertex3f (x + w, y + d + h - 2 * d, 0.);
	glVertex3f (x + w, y + d, 0.);

	// bottom part
	glVertex3f (x, y + h - d, 0.);
	glVertex3f (x + d, y + h, 0.);
	glVertex3f (x + w, y + h, 0.);
	glVertex3f (x + w, y + h - d, 0.);

	glEnd();

	// "Shading"
	float color[4];
	glGetFloatv (GL_CURRENT_COLOR, color);

	glLineWidth(3);
	glColor3f (color[0] * shading_dark, color[1] * shading_dark, color[2] * shading_dark);

	glBegin(GL_LINE_STRIP);
	glVertex3f (x,y + 2, 0.);
	glVertex3f (x,y + h -d, 0.);
	glVertex3f (x + d, y + h, 0.);
	glVertex3f (x + w - 2, y + h, 0.);
	glEnd();

	glColor3f (color[0] * shading_light, color[1] * shading_light, color[2] * shading_light);
	glBegin(GL_LINE_STRIP);
	glVertex3f (x + w - d, y, 0.);
	glVertex3f (x + w, y + d, 0.);
	glEnd();

	glColor4fv (color);
}

/** \brief Draws a label with a given caption at the position (vertically centered)
 *
 * This function draws the label at the horizontal x position and centers the
 * label with regard to the height of the rendered caption.
 *
 * \TODO [med] The vertical alignment around the center of the vertical height
 * of the string is rather unfortunate as different contents will be rendered
 * at different vertical positions (e.g. "aaa" and "ggg") 
 */
void Label (int id, const char* caption, int x, int y) {
	if (caption != NULL) {
		float width, height;
		view = EngineGetView ();
		assert (view);

		view->DrawGLStringMeasure(caption, &width, &height);
		view->DrawGLString(x , y + height * 0.5, caption);
	}
}

/** \brief Draws a label with a given caption centered at the position (vertically centered)
 *
 * This function draws the label at the horizontal x position and centers the
 * label with regard to the height of the rendered caption.
 *
 * \TODO [med] The vertical alignment around the center of the vertical height
 * of the string is rather unfortunate as different contents will be rendered
 * at different vertical positions (e.g. "aaa" and "ggg") 
 */
void LabelCentered (int id, const char* caption, int x, int y) {
	if (caption != NULL) {
		float width, height;
		view = EngineGetView ();
		assert (view);

		SelectFont("console.ttf size=23");
		view->DrawGLStringMeasure(caption, &width, &height);
		view->DrawGLString(x - 0.5 * width, y + height * 0.5, caption);
	}
}

/** \brief Draws a button at the given position
 *
 * The area defined by the parameters defines the clickable area. However
 * decorations might be drawn on a smaller or bigger area.
 *
 * \returns true if it was clicked
 *
 */
bool Button (int id, const char* caption, int x, int y, int w, int h) {
	controller = EngineGetController();
	assert (controller);

//	LogMessage ("id = %d hotitem = %d activeitem = %d kbditem = %d", id, controller->uistate.hotitem, controller->uistate.activeitem, controller->uistate.kbditem);

	// Check for hotness
	if (regionhit (x, y, w, h)) {
		controller->uistate.hotitem = id;
		if (controller->uistate.activeitem == 0 
				&& controller->GetButtonState(MouseButtonLeft))
			controller->uistate.activeitem = id;
	}

	// If nothing is selected
	if (controller->uistate.hotitem != 0) {
		controller->uistate.kbditem = controller->uistate.hotitem;
	}

	if (controller->uistate.kbditem == 0) {
		controller->uistate.hotitem = id;
		controller->uistate.kbditem = id;
	}

	// Render
	glColor3f (0.2, 0.2, 0.2);
//	DrawRoundedBlock (x + 4, y + 4, w, h);

	if (controller->uistate.hotitem == id 
			|| controller->uistate.kbditem == id) {
		if (controller->uistate.activeitem == id) {
			glColor3f (0.8, 0.8, 0.8);
			DrawRoundedBlock (x, y, w, h);
		} else {
			glColor3f (0.7, 0.7, 0.7);
			DrawRoundedBlock (x, y, w, h);
		}
	} else {
		glColor3f (0.4, 0.4, 0.4);
		DrawRoundedBlock (x, y, w, h);
	}

	// Caption
	if (caption != NULL) {
		float width, height;
		view = EngineGetView ();
		assert (view);

		// we have to load the font prior to measuring it
		SelectFont("console.ttf size=23");
		view->DrawGLStringMeasure(caption, &width, &height);

		float xpos = x + w * 0.5 - width * 0.5;
		float ypos = y + h * 0.5 - height * 0.5;
		// LogDebug ("measure '%s' width = %f height = %f", caption, width, height);

		if (controller->uistate.hotitem == id || controller->uistate.kbditem == id) {
			SelectFont("console.ttf size=23 color=#666666");
			view->DrawGLString( xpos - 2., ypos + 2., caption);
			SelectFont("console.ttf size=23 color=#ffffff");
			view->DrawGLString( xpos, ypos, caption);
		}	else {
			SelectFont("console.ttf size=23 color=#4d4d4d");
			view->DrawGLString( xpos - 2., ypos + 2., caption);
			SelectFont("console.ttf size=23 color=#b3b3b3");
			view->DrawGLString( xpos, ypos, caption);
		}
	}

	// Mouse Logic
	if (controller->GetButtonState(MouseButtonLeft) == false
			&& controller->uistate.hotitem == id
			&& controller->uistate.activeitem == id) {
		controller->uistate.lastwidget = id;

		return true;
	}

	// Keyboard Logic
	if (controller->uistate.kbditem == id) {
		// Any unicode keys that were sent get cleared
		controller->uistate.last_unicode = 0;

		// We have to make sure, that we always clear the uistate.last_keysym
		// value, otherwise the same action might be repeated over and over.
		switch (controller->uistate.last_keysym) {
			case SDLK_DOWN:
				controller->uistate.kbditem = 0;
				controller->uistate.hotitem = 0;
				controller->uistate.last_keysym = SDLK_CLEAR;
				break;
			case SDLK_UP:
				controller->uistate.kbditem = controller->uistate.lastwidget;
				controller->uistate.hotitem = controller->uistate.lastwidget;
				controller->uistate.last_keysym = SDLK_CLEAR;
				break;
			case SDLK_RETURN:
				controller->uistate.last_keysym = SDLK_CLEAR;
				// As we (probably) exit the current set of widgets, we have to clear
				// the uistate.kbditem value.
				controller->uistate.kbditem = 0;
				return true;
				break;
		}
	}

	controller->uistate.lastwidget = id;

	return false;
}

/** \brief An Edit widget that allows editing of a string consisting of one line 
 *
 * \TODO [med] vertical alignment (especially of text) is a bit whacky
 *
 * \returns true if it was clicked
 *
 */
bool LineEdit (int id, int x, int y, std::string &text_value, const int &maxlength) {
	controller = EngineGetController();
	assert (controller);

	int textpos_x = x + 8;
	int textpos_y = y + 5;

	y -= 16;
	int w = maxlength * 16;
	int h = 30;

//	LogMessage ("id = %d hotitem = %d activeitem = %d kbditem = %d key = %s", id, controller->uistate.hotitem, controller->uistate.activeitem, controller->uistate.kbditem, convert_keycode (controller->uistate.last_keysym)); 

	// Check for hotness
	if (regionhit (x, y, w, h)) {
		controller->uistate.hotitem = id;
		if (controller->uistate.activeitem == 0 
				&& controller->GetButtonState(MouseButtonLeft)) {
			controller->uistate.activeitem = id;
		}
	}

	if (controller->uistate.kbditem == 0) {
		controller->uistate.hotitem = id;
		controller->uistate.kbditem = id;
		controller->uistate.last_unicode = 0;
		return false;
	}

	// If we have keyboard focus, we highlight the widget
	if ( controller->uistate.kbditem == id) {
		if (controller->uistate.activeitem == id) {
			glColor3f (0.8, 0.8, 0.8);
			DrawBlock (x, y, w, h);
		} else {
			glColor3f (0.7, 0.7, 0.7);
			DrawBlock (x, y, w, h);
		}
	} else {
		glColor3f (0.4, 0.4, 0.4);
		DrawBlock (x, y, w, h);
	}

	// Rendering of the current value 
	float width, height;
	view = EngineGetView ();
	assert (view);

	glColor3f (1., 1., 1.);

	std::string text_output = text_value;

	if (controller->uistate.kbditem == id && SDL_GetTicks() >> 9 & 1)
		text_output += "_";

	SelectFont("console.ttf size=23");
	view->DrawGLStringMeasure(text_value.c_str(), &width, &height);
	view->DrawGLString(textpos_x, textpos_y, text_output.c_str());

	// Keyboard Logic
	if (controller->uistate.kbditem == id) {
		switch (controller->uistate.last_keysym) {
			case SDLK_DOWN:
				controller->uistate.kbditem = 0;
				controller->uistate.hotitem = 0;
				controller->uistate.last_keysym = SDLK_CLEAR;
				break;
			case SDLK_UP:
				controller->uistate.kbditem = controller->uistate.lastwidget;
				controller->uistate.hotitem = controller->uistate.lastwidget;
				controller->uistate.last_keysym = SDLK_CLEAR;
			break;
			case SDLK_CLEAR:
				controller->uistate.last_keysym = SDLK_CLEAR;
				controller->uistate.last_unicode = 0;
				return false;
				break;
			case SDLK_ESCAPE:
				controller->uistate.last_keysym = SDLK_CLEAR;
				controller->uistate.last_unicode = 0;
				controller->uistate.hotitem = 0;
				controller->uistate.kbditem = 0;
				return false;
				break;
			case SDLK_RETURN:
				controller->uistate.last_keysym = SDLK_CLEAR;
				controller->uistate.last_unicode = 0;
				controller->uistate.hotitem = 0;
				controller->uistate.kbditem = 0;
				return true;
				break;
			case SDLK_BACKSPACE:
				if (text_value.size() > 0) {
					text_value = text_value.substr(0, text_value.size() - 1);
					controller->uistate.last_keysym = SDLK_CLEAR;
					controller->uistate.last_unicode = 0;
					return true;
				}
				break;
			default:
				// The raw input processing
				if (maxlength > 0 && text_value.size() < maxlength) {
					if (controller->uistate.last_unicode) {	
						if ((controller->uistate.last_unicode & 0xFF80) == 0) {
							// we do not want to add special characters such as backspaces
							// etc. 
							if ((controller->uistate.last_unicode & 0xFFFF) >= 0x20)
								text_value += controller->uistate.last_unicode & 0x7F;
							controller->uistate.last_unicode = 0;
							return true;
						} else {
							LogWarning ("Input key not supported!");
							return false;
						}
					}
				}
				break;

		}
	}

	controller->uistate.lastwidget = id;

	// Mouse Logic
	if (controller->GetButtonState(MouseButtonLeft) == false
			&& controller->uistate.hotitem == id
			&& controller->uistate.activeitem == id) {
		controller->uistate.kbditem = id;
		controller->uistate.last_unicode = 0;
	}

	return false;
}

float VerticalSlider (int id, int x, int y, int w, int h, float min_value, float max_value, float &value) {
	const int knob_width = 16;
	const int knob_height = h * 2;

	int slider_pos = (w * value) / max_value - knob_width / 2;

	// Check for hotness
	if (regionhit (x, y + h * 0.5 - knob_height * 0.5, w, knob_height)) {
		controller->uistate.hotitem = id;
		if (controller->uistate.activeitem == 0 
				&& controller->GetButtonState(MouseButtonLeft)) {
			controller->uistate.activeitem = id;
		}
	}

	// If nothing is selected
	if (controller->uistate.hotitem != 0) {
		controller->uistate.kbditem = controller->uistate.hotitem;
	}

	if (controller->uistate.kbditem == 0) {
		controller->uistate.hotitem = id;
		controller->uistate.kbditem = id;
	}

	// If we have keyboard focus, we highlight the widget
	if ( controller->uistate.kbditem == id) {
		if (controller->uistate.activeitem == id) {
			glColor3f (0.6, 0.6, 0.6);
			DrawBlock (x, y, w, h);
			glColor3f (0.8, 0.8, 0.8);
			DrawBlock (x + slider_pos, y + h * 0.5 - knob_height * 0.5, knob_width, knob_height);
		} else {
			glColor3f (0.6, 0.6, 0.6);
			DrawBlock (x, y, w, h);
			glColor3f (0.7, 0.7, 0.7);
			DrawBlock (x + slider_pos, y + h * 0.5 - knob_height * 0.5, knob_width, knob_height);
		}
	} else {
		glColor3f (0.4, 0.4, 0.4);
		DrawBlock (x, y, w, h);
		glColor3f (0.5, 0.5, 0.5);
		DrawBlock (x + slider_pos, y + h * 0.5 - knob_height * 0.5, knob_width, knob_height);
	}

	if (controller->uistate.kbditem == id) {
		switch (controller->uistate.last_keysym) {
			case SDLK_DOWN:
				controller->uistate.kbditem = 0;
				controller->uistate.hotitem = 0;
				controller->uistate.last_keysym = SDLK_CLEAR;
				break;
			case SDLK_UP:
				controller->uistate.kbditem = controller->uistate.lastwidget;
				controller->uistate.hotitem = controller->uistate.lastwidget;
				controller->uistate.last_keysym = SDLK_CLEAR;
				break;
			case SDLK_LEFT:
				value -= (max_value - min_value) * 0.1;
				if ( value < min_value)
					value = min_value;
				controller->uistate.last_keysym = SDLK_CLEAR;
				return true;
				break;
			case SDLK_RIGHT:
				value += (max_value - min_value) * 0.1;
				if ( value > max_value)
					value = max_value;
				controller->uistate.last_keysym = SDLK_CLEAR;
				return true;
				break;
		}
	}

	controller->uistate.lastwidget = id;

	if (controller->uistate.activeitem == id) {
		int mouse_pos[2];
		controller->GetMouseScreenPosition(mouse_pos);
		int rel_mouse_pos = mouse_pos[0] - x;
		if (rel_mouse_pos < 0)
			rel_mouse_pos = 0;
		if (rel_mouse_pos > w)
			rel_mouse_pos = w;

		float mouse_value = rel_mouse_pos * max_value / static_cast<float>(w);

		if (mouse_value != value) {
			value = mouse_value;
			return true;
		}

	}

	return false;
}

bool CheckKeyPress (int keycode) {
	if (controller->GetButtonState(keycode))
		return true;

	return false;
}

bool CheckKeyPressed (int keycode) {
	if (controller->uistate.keypressed_set.find(keycode) != controller->uistate.keypressed_set.end())
		return true;

	return false;
}


};
};
