/*  DreamChess
**
**  DreamChess is the legal property of its developers, whose names are too
**  numerous to list here. Please refer to the AUTHORS.txt file distributed
**  with this source distribution.
**
**  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 3 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.
**
**  You should have received a copy of the GNU General Public License
**  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdlib.h>

#include <gamegui/align.h>
#include <gamegui/label.h>
#include <gamegui/option.h>

static gg_colour_t col_grey = {0.5f, 0.5f, 0.5f, 1.0f};

static gg_colour_t col_texthighlight = {0.55f, 0.65f, 0.95f, 1.0f};

static gg_colour_t col_text = {1.0f, 1.0f, 1.0f, 1.0f};

gg_class_id gg_option_get_class_id(void) {
	GG_CHILD(gg_select_get_class_id())
}

#define OPTION_ARROW_LEFT "\253 "
#define OPTION_ARROW_RIGHT " \273"

/** Implements widget::render for option widgets. */
void gg_option_render(gg_widget_t *widget, int x, int y, int focus) {
	gg_option_t *option = GG_OPTION(widget);
	gg_widget_t *child;
	int xx, yy;
	int border_l;
	int border_r;
	int height;

	gg_system_get_string_size(OPTION_ARROW_LEFT, &border_l, &height);
	gg_system_get_string_size(OPTION_ARROW_RIGHT, &border_r, NULL);

	yy = y + option->height_a / 2 - height / 2;

	if (option->sel == -1)
		return;
	if (option->sel > 0) {
		if (!widget->enabled)
			gg_system_draw_string(OPTION_ARROW_LEFT, x, yy, &col_grey, 0, 0);
		else if (focus != GG_FOCUS_NONE)
			gg_system_draw_string(OPTION_ARROW_LEFT, x, yy, &col_texthighlight, 1, 0);
		else
			gg_system_draw_string(OPTION_ARROW_LEFT, x, yy, &col_text, 0, 0);
	} else
		gg_system_draw_string(OPTION_ARROW_LEFT, x, yy, &col_grey, 0, 0);

	xx = x + border_l;

	child = gg_container_get_child(GG_CONTAINER(widget), option->sel);
	child->enabled = widget->enabled;
	child->render(child, xx, y, focus);
	xx = x + option->width_a - border_r;

	if (option->sel < gg_container_get_size(GG_CONTAINER(widget)) - 1) {
		if (!widget->enabled)
			gg_system_draw_string(OPTION_ARROW_RIGHT, xx, yy, &col_grey, 0, 0);
		else if (focus != GG_FOCUS_NONE)
			gg_system_draw_string(OPTION_ARROW_RIGHT, xx, yy, &col_texthighlight, 1, 0);
		else
			gg_system_draw_string(OPTION_ARROW_RIGHT, xx, yy, &col_text, 0, 0);
	} else
		gg_system_draw_string(OPTION_ARROW_RIGHT, xx, yy, &col_grey, 0, 0);
}

/** Implements widget::input for option widgets. */
int gg_option_input(gg_widget_t *widget, gg_event_t event) {
	gg_option_t *option = GG_OPTION(widget);
	gg_select_t *select = GG_SELECT(widget);

	if (option->sel == -1)
		return 0;

	if (event.type == GG_EVENT_KEY && event.key == GG_KEY_RIGHT) {
		if (gg_select_next(select, 0, 0))
			gg_widget_emit_signal(widget, widget, option->changed, NULL);

		return 1;
	}
	if (event.type == GG_EVENT_KEY && event.key == GG_KEY_LEFT) {
		if (gg_select_prev(select, 0, 0))
			gg_widget_emit_signal(widget, widget, option->changed, NULL);

		return 1;
	}
	if (event.type == GG_EVENT_MOUSE && event.mouse.type == GG_MOUSE_BUTTON_DOWN && event.mouse.button == 0) {
		int border_l;
		int border_r;

		gg_system_get_string_size(OPTION_ARROW_LEFT, &border_l, NULL);
		gg_system_get_string_size(OPTION_ARROW_RIGHT, &border_r, NULL);

		if (event.mouse.x < border_l && gg_select_prev(select, 0, 0))
			gg_widget_emit_signal(widget, widget, option->changed, NULL);

		if (event.mouse.x >= widget->width_a - border_l && gg_select_next(select, 0, 0))
			gg_widget_emit_signal(widget, widget, option->changed, NULL);

		return 1;
	}

	return 0;
}

void gg_option_set_size(gg_widget_t *widget, int width, int height) {
	gg_container_t *container = GG_CONTAINER(widget);
	int border_l;
	int border_r;
	int i;

	gg_system_get_string_size(OPTION_ARROW_LEFT, &border_l, NULL);
	gg_system_get_string_size(OPTION_ARROW_RIGHT, &border_r, NULL);

	for (i = 0; i < gg_container_get_size(container); i++) {
		gg_widget_t *child = gg_container_get_child(container, i);
		child->set_size(child, width - border_l - border_r, height);
	}

	gg_set_size(widget, width, height);
}

void gg_option_init(gg_option_t *option) {
	gg_select_init((gg_select_t *)option);

	option->render = gg_option_render;
	option->input = gg_option_input;
	option->set_size = gg_option_set_size;
	option->id = gg_option_get_class_id();
	option->changed = gg_signal_lookup(option->id, "option_changed");
	if (option->changed == -1)
		option->changed = gg_signal_register(option->id, "option_changed");
}

/** @brief Creates an option widget.
 *
 *  An option widget consists of a label and a set of options. The label is
 *  rendered to the left of the currently selected option. The input handler
 *  allows for cycling through the available options.
 *
 *  @return The created widget.
 */
gg_widget_t *gg_option_create(void) {
	gg_option_t *option = malloc(sizeof(gg_option_t));

	gg_option_init(option);

	return GG_WIDGET(option);
}

/** @brief Appends an option to the option widget's option list.
 *
 *  @param widget The option widget.
 *  @param string The option to append.
 */
void gg_option_append(gg_option_t *option, gg_widget_t *child) {
	int width, child_height;
	int height;
	int border_l;
	int border_r;

	child->get_requested_size(child, &width, &child_height);

	gg_system_get_string_size(OPTION_ARROW_LEFT, &border_l, &height);
	gg_system_get_string_size(OPTION_ARROW_RIGHT, &border_r, NULL);

	width += border_l + border_r;

	if (child_height > height)
		height = child_height;

	gg_container_append(GG_CONTAINER(option), child);

	if (width > option->width)
		option->width = width;

	if (height > option->height)
		option->height = height;

	if (gg_container_get_size(GG_CONTAINER(option)) == 2)
		option->enabled = 1;

	if (option->sel == -1)
		option->sel = 0;
}

void gg_option_append_label(gg_option_t *option, char *text, float xalign, float yalign) {
	gg_widget_t *label = gg_label_create(text);
	gg_align_set_alignment(GG_ALIGN(label), xalign, yalign);
	gg_option_append(option, label);
}

/** @brief Returns the index of the selected option of an option widget.
 *
 *  @param widget The option widget.
 *  @return Index of the selected option.
 */
int gg_option_get_selected(gg_option_t *option) {
	return option->sel;
}

void gg_option_set_selected(gg_option_t *option, int index) {
	option->sel = index;
}
