#ifndef MENUITEMS_H
#define MENUITEMS_H

#include "varyingmenu.h"
#include "lcd.h"
#include "common.h"
#include <boost/lexical_cast.hpp>
#include <string>
#include <GL/gl.h>
#include <GL/glu.h>
#include <algorithm>

namespace menuitems {

	extern GLfloat hlColor[4];
	
template<class IntT>
class IntGetter: public MenuItem {
public:
	IntGetter(IntT a = IntT()):
		m_value(a)
		{}
	
	bool handleKey(SDLKey key) {
		if (key == SDLK_BACKSPACE)
			m_value /= 10;
		else if (isdigit(key))
			m_value = m_value*10 + (key-'0');
		return 1;
	}
	void draw() {
		char buf[64] = {""};
		if (m_value)
			snprintf(buf, sizeof(buf), "%i", m_value);
		LCD::print(buf);
	}
	void setValue(IntT a) {
		m_value = a;
	}
	IntT getValue() {
		return m_value;
	}

private:
	IntT m_value;
};

template<class FloatT>
class FloatGetter: public MenuItem {
public:
	FloatGetter(FloatT a = FloatT())
	{
		setValue(a);
		m_dot = m_value.find_first_of('.') != std::string::npos;
	}
	
	bool handleKey(SDLKey key) {
		if (key == SDLK_BACKSPACE) {
			if (!m_value.empty()) {
				if (m_value[m_value.size()-1] == '.')
					m_dot = 0;
				m_value.erase(m_value.end()-1);
			}
		} else if (isdigit(key)) {
			m_value.push_back(key);
		} else if (!m_dot && key=='.') {
			m_dot = 1;
			m_value.push_back('.');
		}
		return 1;
	}
	void draw() {
		LCD::print(m_value.c_str());
	}
	FloatT getValue() {
		return boost::lexical_cast<FloatT>(m_value);
	}
	void setValue(FloatT a) {
		m_value = boost::lexical_cast<std::string>(a);
	}

private:
	std::string m_value;
	bool m_dot;
};

class BoolGetter: public MenuItem {
public:
	BoolGetter(bool a=0):
		m_value(a)
		{}
	bool handleKey(SDLKey key) {
		if (key==SDLK_LEFT || key==SDLK_RIGHT)
			m_value = !m_value;
		return 1;
	}
	void draw() {
		glPushMatrix();
		const char* const str[] = {"yes", "no"};
		for(int i=0; i<2; ++i) {
			if (i == !m_value) {
				GLfloat width;
				glGetFloatv(GL_LINE_WIDTH, &width);
				GLfloat color[4];
				glGetFloatv(GL_CURRENT_COLOR, color);
				glLineWidth(std::max(width*1.5f, width+2.0f));
				glColor4fv(hlColor);
				LCD::print(str[i]);
				glLineWidth(width);
				glColor4fv(color);
			}
			LCD::print(str[i]);
			glTranslatef(0.6f*(strlen(str[i])+1), 0, 0);
		}
		glPopMatrix();
	}
	bool getValue() {
		return m_value;
	}
	void setValue(bool a) {
		m_value = a;
	}

private:
	bool m_value;
};

class StringPicker: public MenuItem {
public:
	StringPicker(): activeItem(0) {}
	void add(const std::string& str) {
		m_strings.push_back(str);
	}
	void setActive(int num) {
		activeItem = num;
	}
	int getActive() {
		return activeItem;
	}

	bool handleKey(SDLKey key) {
		if (key == SDLK_LEFT) {
			if (--activeItem < 0)
				activeItem = m_strings.size()-1;
		}
		else if (key == SDLK_RIGHT) {
			if (uint(++activeItem) >= m_strings.size())
				activeItem = 0;
		}
		return 1;
	}
	void draw() {
		GLdouble modelview[16];
		GLdouble projection[16];
		GLint viewport[4];
		glGetDoublev(GL_PROJECTION_MATRIX, projection);
		glGetIntegerv(GL_VIEWPORT, viewport);
		glPushMatrix();
		for(uint i=0; i<m_strings.size(); ++i) {
			glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
			GLdouble x,y,z;
			gluProject(m_strings[i].size()*0.6, 0, 0, modelview, projection, viewport, &x, &y, &z);
			if (x > viewport[2]) {
				glPopMatrix();
				glTranslatef(0, -1.2, 0);
				glPushMatrix();
			}
			if (int(i) == activeItem) {
				GLfloat width;
				glGetFloatv(GL_LINE_WIDTH, &width);
				GLfloat color[4];
				glGetFloatv(GL_CURRENT_COLOR, color);
				glLineWidth(std::max(width*1.5f, width+2.0f));
				glColor4fv(hlColor);
				LCD::print(m_strings[i].c_str());
				glLineWidth(width);
				glColor4fv(color);
			}
			LCD::print(m_strings[i].c_str());
			glTranslatef(0.6f*(m_strings[i].size()+1), 0, 0);
		}
		glPopMatrix();
	}
	
private:
	typedef std::vector<std::string> StrV;
	StrV m_strings;
	int activeItem;
};

}	// namespace menuitems

#endif
