/* --------------------------------------------------------------------
EXTREME TUXRACER

Copyright (C) 1999-2001 Jasmin F. Patry (Tuxracer)
Copyright (C) 2010 Extreme Tuxracer Team

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

#ifdef HAVE_CONFIG_H
#include <etr_config.h>
#endif

#include "gui.h"
#include "textures.h"
#include "font.h"
#include "ogl.h"
#include "winsys.h"
#include <vector>


#define CURSOR_SIZE 10

static std::vector<TWidget*> Widgets;
static int lock_focussed = -1;
static int focussed = -1;
static bool locked_LR = false;
static bool locked_UD = false;

static TWidget* AddWidget(TWidget* widget) {
	if (Widgets.size() == focussed) {
		widget->focus = true;
		widget->Focussed();
	}
	Widgets.push_back(widget);
	return widget;
}

static bool Inside(int x, int y, const TRect& Rect) {
	return (x >= Rect.left
	        && x <= Rect.left + Rect.width
	        && y >= Rect.top
	        && y <= Rect.top + Rect.height);
}

TWidget::TWidget(int x, int y, int width, int height, bool interactive_)
	: active(true)
	, visible(true)
	, interactive(interactive_)
	, focus(false) {
	mouseRect.top = y;
	mouseRect.left = x;
	mouseRect.height = height;
	mouseRect.width = width;
	position.x = x;
	position.y = y;
}

bool TWidget::Click(int x, int y) {
	return active && visible && Inside(x, y, mouseRect);
}

void TWidget::MouseMove(int x, int y) {
	bool ofocus = focus;
	focus = interactive && active && visible && Inside(x, y, mouseRect);
	if (ofocus != focus)
		Focussed();
}


TLabel::TLabel(const sf::String& string, int x, int y, const sf::Color& color)
	: TWidget(x, y, 0, 0, false)
	, text(string, FT.getCurrentFont(), FT.GetSize()) {
	if (x == CENTER)
		text.setPosition((Winsys.resolution.width - text.getLocalBounds().width) / 2, y);
	else
		text.setPosition(x, y);
	text.setFillColor(color);
	text.setOutlineColor(color);
}

void TLabel::Focussed(bool masterFocus) {
	focus = masterFocus && active;
	if (focus) {
		text.setFillColor(colDYell);
		text.setOutlineColor(colDYell);
	} else {
		text.setFillColor(colWhite);
		text.setOutlineColor(colWhite);
	}
}

void TLabel::Draw() const {
	Winsys.draw(text);
}

sf::Vector2f TLabel::GetSize() const {
	return sf::Vector2f(text.getLocalBounds().width, text.getLocalBounds().height);

}

TLabel* AddLabel(const sf::String& string, int x, int y, const sf::Color& color) {
	return static_cast<TLabel*>(AddWidget(new TLabel(string, x, y, color)));
}


TFramedText::TFramedText(int x, int y, int width, int height, int line, const sf::Color& backcol, const sf::String& string, unsigned int ftsize, bool borderFocus_)
	: TWidget(x, y, width, height, false)
	, frame(sf::Vector2f(width - line * 2, height - line * 2))
	, text(string, FT.getCurrentFont(), ftsize)
	, borderFocus(borderFocus_) {
	text.setPosition(x + line + 20, y + line);
	if (!borderFocus) {
		text.setFillColor(colWhite);
		text.setOutlineColor(colWhite);
	} else {
		text.setFillColor(colDYell);
		text.setOutlineColor(colDYell);
	}
	frame.setPosition(x + line, y + line);
	frame.setOutlineThickness(line);
	frame.setFillColor(backcol);
	frame.setOutlineColor(colWhite);
}

void TFramedText::Activated() {
	if (!active) {
		text.setFillColor(colLGrey);
		text.setOutlineColor(colLGrey);
	} else if (borderFocus || focus) {
		text.setFillColor(colDYell);
		text.setOutlineColor(colDYell);
	} else {
		text.setFillColor(colWhite);
		text.setOutlineColor(colWhite);
	}
}

void TFramedText::Focussed(bool masterFocus) {
	focus = masterFocus && active;
	if (focus) {
		frame.setOutlineColor(colDYell);
		if (!borderFocus) {
			text.setFillColor(colDYell);
			text.setOutlineColor(colDYell);
		}
	} else {
		frame.setOutlineColor(colWhite);
		if (!borderFocus) {
			text.setFillColor(colWhite);
			text.setOutlineColor(colWhite);
		}
	}
}

void TFramedText::Draw() const {
	Winsys.draw(frame);
	Winsys.draw(text);
}

TFramedText* AddFramedText(int x, int y, int width, int height, int line, const sf::Color& backcol, const sf::String& text, unsigned int ftsize, bool borderFocus) {
	return static_cast<TFramedText*>(AddWidget(new TFramedText(x, y, width, height, line, backcol, text, ftsize, borderFocus)));
}

TTextButton::TTextButton(int x, int y, const sf::String& text_, int ftsize)
	: TWidget(x, y, 0, 0)
	, text(text_, FT.getCurrentFont(), ftsize) {
	if (ftsize < 0) text.setCharacterSize(FT.AutoSizeN(4));

	int len = text.getLocalBounds().width;
	if (x == CENTER) position.x = (Winsys.resolution.width - len) / 2;
	text.setPosition(position.x, position.y);
	int offs = ftsize / 5;
	mouseRect.left = position.x-20;
	mouseRect.top = position.y+offs;
	mouseRect.width = len+40;
	mouseRect.height = ftsize+offs;
}

void TTextButton::Focussed() {
	if (focus) {
		text.setFillColor(colDYell);
		text.setOutlineColor(colDYell);
	} else {
		text.setFillColor(colWhite);
		text.setOutlineColor(colWhite);
	}
}

void TTextButton::Draw() const {
	Winsys.draw(text);
}

TTextButton* AddTextButton(const sf::String& text, int x, int y, int ftsize) {
	return static_cast<TTextButton*>(AddWidget(new TTextButton(x, y, text, ftsize)));
}

TTextButton* AddTextButtonN(const sf::String& text, int x, int y, int rel_ftsize) {
	unsigned int siz = FT.AutoSizeN(rel_ftsize);
	return AddTextButton(text, x, y, siz);
}


TTextField::TTextField(int x, int y, int width, int height, const sf::String& text_)
	: TWidget(x, y, width, height)
	, text(text_, FT.getCurrentFont(), FT.AutoSizeN(5))
	, frame(sf::Vector2f(width-6.f, height-6.f))
	, cursorShape(sf::Vector2f(2.f, 30.f * Winsys.scale))
	, maxLng(32)
	, time(0.0)
	, cursor(false) {
	text.setPosition(mouseRect.left + 20, mouseRect.top);
	cursorShape.setFillColor(colYellow);
	frame.setPosition(x + 3, y + 3);
	frame.setOutlineThickness(3);
	frame.setFillColor(colMBackgr);
	frame.setOutlineColor(colWhite);
	SetCursorPos(0);
}

void TTextField::Draw() const {
	Winsys.draw(frame);
	Winsys.draw(text);
	if (cursor && focus)
		Winsys.draw(cursorShape);
}

void TTextField::TextEnter(char c) {
	if (c != '\b') {
		sf::String string = text.getString();
		string.insert(cursorPos, c);
		text.setString(string);
		SetCursorPos(cursorPos+1);
	}
}

void TTextField::SetCursorPos(std::size_t new_pos) {
	cursorPos = new_pos;
	cursorShape.setPosition(text.findCharacterPos(cursorPos).x, mouseRect.top + 9);
}

void TTextField::Focussed() {
	if (focus) {
		text.setFillColor(colDYell);
		text.setOutlineColor(colDYell);
		frame.setOutlineColor(colDYell);
	} else {
		text.setFillColor(colWhite);
		text.setOutlineColor(colWhite);
		frame.setOutlineColor(colWhite);
	}
}

bool TTextField::Click(int x, int y) {
	if (TWidget::Click(x, y)) {
		cursorPos = 0;
		float first = text.findCharacterPos(cursorPos).x;
		for (;;) {
			float second = text.findCharacterPos(cursorPos + 1).x;
			if ((first + second) / 2.f >= x || cursorPos >= text.getString().getSize())
				break;
			cursorPos++;
			first = second;
		}
		cursorShape.setPosition(text.findCharacterPos(cursorPos).x, mouseRect.top + 9);
		return true;
	}
	return false;
}

static void eraseFromText(sf::Text& text, std::size_t pos) {
	sf::String str = text.getString();
	str.erase(pos, 1);
	text.setString(str);
}
void TTextField::Key(sf::Keyboard::Key key, bool released) {
	switch (key) {
		case sf::Keyboard::Delete:
			if (cursorPos < text.getString().getSize()) eraseFromText(text, cursorPos);
			break;
		case sf::Keyboard::BackSpace:
			if (cursorPos > 0) { eraseFromText(text, cursorPos-1); SetCursorPos(cursorPos - 1); }
			break;
		case sf::Keyboard::Right:
			if (cursorPos < text.getString().getSize()) SetCursorPos(cursorPos + 1);
			break;
		case sf::Keyboard::Left:
			if (cursorPos > 0) SetCursorPos(cursorPos - 1);
			break;
		case sf::Keyboard::Home:
			SetCursorPos(0);
			break;
		case sf::Keyboard::End:
			SetCursorPos(text.getString().getSize());
			break;
		default:
			break;
	}
}

void TTextField::UpdateCursor(float timestep) {
	time += timestep;
	if (time > CRSR_PERIODE) {
		time = 0;
		cursor = !cursor;
	}
}

TTextField* AddTextField(const sf::String& text, int x, int y, int width, int height) {
	locked_LR = true;
	return static_cast<TTextField*>(AddWidget(new TTextField(x, y, width, height, text)));
}

TCheckbox::TCheckbox(int x, int y, int width, const sf::String& tag_)
	: TWidget(x, y, 32 * Winsys.scale / 0.8f, 32 * Winsys.scale / 0.8f)
	, text(tag_, FT.getCurrentFont(), FT.GetSize())
	, back(Tex.GetSFTexture(CHECKBOX))
	, checkmark(Tex.GetSFTexture(CHECKMARK_SMALL))
	, checked(false) {
	text.setPosition(x, y);
	back.setPosition(x + width - 32, y);
	checkmark.setPosition(x + width - 32, y);
	mouseRect.left = x + width - 32;
	back.setScale(Winsys.scale / 0.8f, Winsys.scale / 0.8f);
	checkmark.setScale(Winsys.scale / 0.8f, Winsys.scale / 0.8f);
}

void TCheckbox::SetPosition(int x, int y) {
	text.setPosition(x, y);
	back.setPosition(x, y);
	checkmark.setPosition(x, y);
}

void TCheckbox::Focussed() {
	if (focus) {
		text.setFillColor(colDYell);
		text.setOutlineColor(colDYell);
	} else {
		text.setFillColor(colWhite);
		text.setOutlineColor(colWhite);
	}
}

void TCheckbox::Draw() const {
	Winsys.draw(back);
	if (checked)
		Winsys.draw(checkmark);
	Winsys.draw(text);
}

bool TCheckbox::Click(int x, int y) {
	if (active && visible && Inside(x, y, mouseRect)) {
		checked = !checked;
		return true;
	}
	return false;
}

void TCheckbox::Key(sf::Keyboard::Key key, bool released) {
	if (released) return;

	if (key == sf::Keyboard::Space || key == sf::Keyboard::Return) {
		checked = !checked;
	}
}

TCheckbox* AddCheckbox(int x, int y, int width, const sf::String& tag) {
	return static_cast<TCheckbox*>(AddWidget(new TCheckbox(x, y, width, tag)));
}

TIconButton::TIconButton(int x, int y, const sf::Texture& texture, float size_, int max_, int value_)
	: TWidget(x, y, 32, 32)
	, sprite(texture)
	, frame(sf::Vector2f(size_, size_))
	, size(size_)
	, maximum(max_)
	, value(value_) {
	sprite.setScale(size / (texture.getSize().x / 2.f), size / (texture.getSize().y / 2.f));
	sprite.setPosition(x, y);
	frame.setPosition(x, y);
	frame.setOutlineColor(colWhite);
	frame.setOutlineThickness(3.f);
	SetValue(value_);
}

void TIconButton::SetValue(int _value) {
	value = _value;
	if (value > maximum)
		value = 0;
	else if (value < 0)
		value = maximum;

	sf::Vector2u texSize = sprite.getTexture()->getSize();
	switch (value) {
		case 0:
			sprite.setTextureRect(sf::IntRect(0, 0, texSize.x / 2, texSize.y / 2));
			break;
		case 1:
			sprite.setTextureRect(sf::IntRect(texSize.x / 2, 0, texSize.x / 2, texSize.y / 2));
			break;
		case 2:
			sprite.setTextureRect(sf::IntRect(0, texSize.y / 2, texSize.x / 2, texSize.y / 2));
			break;
		case 3:
			sprite.setTextureRect(sf::IntRect(texSize.x / 2, texSize.y / 2, texSize.x / 2, texSize.y / 2));
			break;
	}
}

void TIconButton::Draw() const {
	Winsys.draw(frame);
	Winsys.draw(sprite);
}

void TIconButton::Focussed() {
	if (focus)
		frame.setOutlineColor(colDYell);
	else
		frame.setOutlineColor(colWhite);
}

bool TIconButton::Click(int x, int y) {
	if (Inside(x, y, mouseRect)) {
		SetValue(value + 1);
		return true;
	}
	return false;
}

void TIconButton::Key(sf::Keyboard::Key key, bool released) {
	if (released) return;

	if (key == sf::Keyboard::Down) { // Arrow down/left
		SetValue(value - 1);
	} else if (key == sf::Keyboard::Up) { // Arrow up/right
		SetValue(value + 1);
	}
}

TIconButton* AddIconButton(int x, int y, const sf::Texture& texture, float size, int maximum, int value) {
	locked_UD = true;
	return static_cast<TIconButton*>(AddWidget(new TIconButton(x, y, texture, size, maximum, value)));
}

TArrow::TArrow(int x, int y, bool down_)
	: TWidget(x, y, 32 * Winsys.scale / 0.8f, 16 * Winsys.scale / 0.8f)
	, sprite(Tex.GetSFTexture(LB_ARROWS))
	, down(down_) {
	sprite.setPosition(x, y);
	sprite.setScale(Winsys.scale / 0.8f, Winsys.scale / 0.8f);

	SetTexture();
}

void TArrow::Focussed() {
	SetTexture();
}

void TArrow::Activated() {
	SetTexture();
}

void TArrow::SetTexture() {
	static const float textl[6] = { 0.5f, 0.f, 0.5f, 0.5f, 0.f, 0.5f };
	static const float texbr[6] = { 0.5f, 0.5f, 0.f, 0.75f, 0.75f, 0.25f };

	int type = 0;
	if (active)
		type = 1;
	if (focus)
		type++;
	if (down)
		type += 3;

	sf::Vector2u texSize = sprite.getTexture()->getSize();
	sprite.setTextureRect(sf::IntRect(textl[type] * texSize.x, texbr[type] * texSize.y, texSize.x / 2, texSize.y / 4));
}

void TArrow::Draw() const {
	Winsys.draw(sprite);
}

TArrow* AddArrow(int x, int y, bool down) {
	return static_cast<TArrow*>(AddWidget(new TArrow(x, y, down)));
}


TUpDown::TUpDown(int x, int y, int min_, int max_, int value_, int distance, bool swapArrows_)
	: TWidget(x, y, 32 * Winsys.scale / 0.8f, (32 + distance)*Winsys.scale / 0.8f)
	, up(x, y + (16 + distance)*Winsys.scale / 0.8f, true)
	, down(x, y, false)
	, higher(swapArrows_ ? up : down)
	, lower(swapArrows_ ? down : up)
	, value(value_)
	, minimum(min_)
	, maximum(max_)
	, swapArrows(swapArrows_) {
	lower.SetActive(value < maximum);
	higher.SetActive(value > minimum);
}

void TUpDown::Draw() const {
	up.Draw();
	down.Draw();
}

bool TUpDown::Click(int x, int y) {
	if (active && visible && lower.Click(x, y)) {
		value++;
		higher.SetActive(true);
		if (value == maximum)
			lower.SetActive(false);
		return true;
	}
	if (active && visible && higher.Click(x, y)) {
		lower.SetActive(true);
		value--;
		if (value == minimum)
			down.SetActive(false);
		return true;
	}
	return false;
}

void TUpDown::Key(sf::Keyboard::Key key, bool released) {
	if (released) return;

	if ((!swapArrows && key == sf::Keyboard::Up) || (swapArrows && key == sf::Keyboard::Down)) { // Arrow up
		if (value > minimum) {
			value--;
			lower.SetActive(true);
			if (value == minimum)
				higher.SetActive(false);
		}
	} else if ((!swapArrows && key == sf::Keyboard::Down) || (swapArrows && key == sf::Keyboard::Up)) { // Arrow down
		if (value < maximum) {
			value++;
			higher.SetActive(true);
			if (value == maximum)
				lower.SetActive(false);
		}
	}
}

void TUpDown::MouseMove(int x, int y) {
	bool ofocus = focus;
	focus = active && visible && Inside(x, y, mouseRect);
	if (ofocus != focus)
		Focussed();
	up.MouseMove(x, y);
	down.MouseMove(x, y);
}

void TUpDown::SetValue(int value_) {
	value = clamp(minimum, value_, maximum);
	lower.SetActive(value < maximum);
	higher.SetActive(value > minimum);
}
void TUpDown::SetMinimum(int min_) {
	minimum = min_;
	value = clamp(minimum, value, maximum);
	lower.SetActive(value < maximum);
	higher.SetActive(value > minimum);
}
void TUpDown::SetMaximum(int max_) {
	maximum = max_;
	value = clamp(minimum, value, maximum);
	lower.SetActive(value < maximum);
	higher.SetActive(value > minimum);
}

TUpDown* AddUpDown(int x, int y, int minimum, int maximum, int value, int distance, bool swapArrows) {
	locked_UD = true;
	return static_cast<TUpDown*>(AddWidget(new TUpDown(x, y, minimum, maximum, value, distance, swapArrows)));
}

// ------------------ Elementary drawing ---------------------------------------------

void DrawFrameX(int x, int y, int w, int h, int line, const sf::Color& backcol, const sf::Color& framecol, float transp) {
	x += line;
	y += line;
	w -= line * 2;
	h -= line * 2;
	sf::RectangleShape shape(sf::Vector2f(w, h));
	shape.setPosition(x, y);
	shape.setOutlineThickness(line);
	shape.setFillColor(sf::Color(backcol.r, backcol.g, backcol.b, backcol.a * transp));
	shape.setOutlineColor(sf::Color(framecol.r, framecol.g, framecol.b, framecol.a * transp));
	Winsys.draw(shape);
}

void DrawBonusExt(int y, std::size_t numraces, std::size_t num) {
	std::size_t maxtux = numraces * 3;
	if (num > maxtux) return;

	static const sf::Color col2(115, 166, 217);

	int lleft[3];

	int framewidth = (int)numraces * 40 + 8;
	int totalwidth = framewidth * 3 + 8;
	int xleft = (Winsys.resolution.width - totalwidth) / 2;
	lleft[0] = xleft;
	lleft[1] = xleft + framewidth + 4;
	lleft[2] = xleft + framewidth + framewidth + 8;

	DrawFrameX(lleft[0], y, framewidth, 40, 1, col2, colBlack, 1);
	DrawFrameX(lleft[1], y, framewidth, 40, 1, col2, colBlack, 1);
	DrawFrameX(lleft[2], y, framewidth, 40, 1, col2, colBlack, 1);

	static sf::Sprite tuxbonus(Tex.GetSFTexture(TUXBONUS));
	sf::Vector2u size = tuxbonus.getTexture()->getSize();
	tuxbonus.setTextureRect(sf::IntRect(0, 0, size.x, size.y/2));

	for (std::size_t i=0; i<maxtux; i++) {
		std::size_t majr = (i/numraces);
		std::size_t minr = i - majr * numraces;
		if (majr > 2) majr = 2;
		int x = lleft[majr] + (int)minr * 40 + 6;

		if (i<num) {
			tuxbonus.setPosition(x, y + 4);
			Winsys.draw(tuxbonus);
		}
	}
}

void DrawGUIFrame() {
	static sf::Sprite bottom_left(Tex.GetSFTexture(BOTTOM_LEFT));
	static sf::Sprite bottom_right(Tex.GetSFTexture(BOTTOM_RIGHT));
	static sf::Sprite top_left(Tex.GetSFTexture(TOP_LEFT));
	static sf::Sprite top_right(Tex.GetSFTexture(TOP_RIGHT));

	bottom_left.setPosition(0, Winsys.resolution.height - bottom_left.getTexture()->getSize().y);
	bottom_right.setPosition(Winsys.resolution.width - bottom_right.getTexture()->getSize().x, Winsys.resolution.height - bottom_right.getTexture()->getSize().y);
	top_right.setPosition(Winsys.resolution.width - top_right.getTexture()->getSize().x, 0);

	Winsys.draw(bottom_left);
	Winsys.draw(bottom_right);
	Winsys.draw(top_left);
	Winsys.draw(top_right);
}

void DrawGUIBackground(float scale) {
	DrawGUIFrame();

	static sf::Sprite logo(Tex.GetSFTexture(T_TITLE));
	scale *= 0.5f;
	logo.setScale(scale, scale);
	logo.setPosition((Winsys.resolution.width - logo.getTextureRect().width*scale)/2, 5);
	Winsys.draw(logo);
}

void DrawCursor() {
	static sf::Sprite s(Tex.GetSFTexture(MOUSECURSOR));
	static bool init = false;
	if (!init) {
		s.setScale((double) Winsys.resolution.width / 1400, (double) Winsys.resolution.width / 1400);
		init = true;
	}
	s.setPosition(cursor_pos.x, cursor_pos.y);
	Winsys.draw(s);
}


// ------------------ Main GUI functions ---------------------------------------------

void DrawGUI() {
	for (std::size_t i = 0; i < Widgets.size(); i++)
		if (Widgets[i]->GetVisible())
			Widgets[i]->Draw();
	if (param.ice_cursor)
		DrawCursor();
}

TWidget* ClickGUI(int x, int y) {
	TWidget* clicked = nullptr;
	for (std::size_t i = 0; i < Widgets.size(); i++) {
		if (Widgets[i]->Click(x, y)) {
			clicked = Widgets[i];
			lock_focussed = focussed;
		}
	}
	return clicked;
}

TWidget* MouseMoveGUI(int x, int y) {
	if (x != 0 || y != 0) {
		focussed = -1;
		for (std::size_t i = 0; i < Widgets.size(); i++) {
			Widgets[i]->MouseMove(cursor_pos.x, cursor_pos.y);
			if (Widgets[i]->focussed())
				focussed = (int)i;
		}
	}
	if (focussed == -1) {
		focussed = lock_focussed;
		if (focussed != -1) {
			Widgets[focussed]->focus = true;
			Widgets[focussed]->Focussed();
		}
		return 0;
	}

	return Widgets[focussed];
}

TWidget* KeyGUI(sf::Keyboard::Key key, bool released) {
	if (!released) {
		switch (key) {
			case sf::Keyboard::Tab:
				if (sf::Keyboard::isKeyPressed(sf::Keyboard::LShift) || sf::Keyboard::isKeyPressed(sf::Keyboard::RShift))
					DecreaseFocus();
				else
					IncreaseFocus();
				break;
			case sf::Keyboard::Up:
				if (!locked_UD)
					DecreaseFocus();
				break;
			case sf::Keyboard::Left:
				if (!locked_LR)
					DecreaseFocus();
				break;
			case sf::Keyboard::Down:
				if (!locked_UD)
					IncreaseFocus();
				break;
			case sf::Keyboard::Right:
				if (!locked_LR)
					IncreaseFocus();
				break;
			default:
				break;
		}
	}
	if (focussed == -1)
		return 0;
	Widgets[focussed]->Key(key, released);
	return Widgets[focussed];
}

TWidget* TextEnterGUI(char text) {
	if (focussed == -1)
		return 0;
	Widgets[focussed]->TextEnter(text);
	return Widgets[focussed];
}

void SetFocus(TWidget* widget) {
	if (!widget)
		focussed = -1;
	else
		for (std::size_t i = 0; i < Widgets.size(); i++) {
			if (Widgets[i] == widget) {
				Widgets[i]->focus = true;
				Widgets[i]->Focussed();
				focussed = (int)i;
				break;
			} else if (Widgets[i]->focus) {
				Widgets[i]->focus = false;
				Widgets[i]->Focussed();
			}
		}
}

void IncreaseFocus() {
	if (focussed >= 0) {
		Widgets[focussed]->focus = false;
		Widgets[focussed]->Focussed();
	}

	focussed++;
	if (focussed >= (int)Widgets.size())
		focussed = 0;
	int end = focussed;
	// Select only active widgets
	do {
		if (Widgets[focussed]->GetActive() && Widgets[focussed]->GetInteractive())
			break;

		focussed++;
		if (focussed >= (int)Widgets.size())
			focussed = 0;
	} while (end != focussed);

	if (focussed >= 0) {
		Widgets[focussed]->focus = true;
		Widgets[focussed]->Focussed();
	}
	lock_focussed = focussed;
}
void DecreaseFocus() {
	if (focussed >= 0) {
		Widgets[focussed]->focus = false;
		Widgets[focussed]->Focussed();
	}

	if (focussed > 0)
		focussed--;
	else
		focussed = (int)Widgets.size()-1;
	int end = focussed;
	// Select only active widgets
	do {
		if (Widgets[focussed]->GetActive() && Widgets[focussed]->GetInteractive())
			break;

		if (focussed > 0)
			focussed--;
		else
			focussed = (int)Widgets.size()-1;
	} while (end != focussed);

	if (focussed >= 0) {
		Widgets[focussed]->focus = true;
		Widgets[focussed]->Focussed();
	}
	lock_focussed = focussed;
}

void ResetGUI() {
	for (std::size_t i = 0; i < Widgets.size(); i++)
		delete Widgets[i];
	Widgets.clear();
	focussed = 0;
	lock_focussed = -1;
	locked_LR = locked_UD = false;
}

// ---------------------------------------------------------------

int AutoYPosN(int percent) {
	return Winsys.resolution.height * percent / 100;
}

TArea AutoAreaN(int top_perc, int bott_perc, unsigned int w) {
	TArea res;
	res.top = AutoYPosN(top_perc);
	res.bottom = AutoYPosN(bott_perc);
	if (w > Winsys.resolution.width) w = Winsys.resolution.width;
	res.left = (Winsys.resolution.width - w) / 2;
	res.right = Winsys.resolution.width - res.left;
	return res;
}
