/*
 * Kangaroo Punch MultiPlayer Game Server Mark II
 * Copyright (C) 2020-2021  Scott Duensing
 *
 * 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 <https://www.gnu.org/licenses/>.
 *
 */


#include "radio.h"


static void    radioDel(WidgetT **widget);
static void    radioMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event);
static void    radioPaint(WidgetT *widget, uint8_t enabled, RectT pos);
static void    radioSelectedInGroupClear(WidgetT *widget, uint32_t group);
static RadioT *radioSelectedInGroupFind(WidgetT *widget, uint32_t group);


void radioClickHandlerSet(RadioT *radio, widgetCallback callback) {
	radio->clicked = callback;
}


static void radioDel(WidgetT **widget) {
	RadioT *r = (RadioT *)*widget;

	if (r->title) free(r->title);
}


WidgetT *radioInit(WidgetT *widget, char *title, uint16_t group) {
	RadioT *r = (RadioT *)widget;

	r->base.delMethod        = radioDel;
	r->base.paintMethod      = radioPaint;
	r->base.mouseEventMethod = radioMouseEvent;
	r->title                 = NULL;
	r->clicked               = NULL;
	r->group                 = group;

	radioTitleSet(r, title);

	// Width is set in radioSetTitle
	r->base.pos.h = fontHeightGet(_guiFont);

	return widget;
}


static void radioMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event) {
	RadioT *r = (RadioT *)widget;

	(void)x;
	(void)y;
	(void)mouse;

	if (event == MOUSE_EVENT_LEFT_UP) {
		// Select us.
		radioSelectedSet(r);
		// Fire callback on mouse up.
		if (r->clicked) guiPendingEventAdd(widget, r->clicked);
	}
}


RadioT *radioNew(uint16_t x, uint16_t y, char *title, uint16_t group) {
	RadioT  *radio  = (RadioT *)malloc(sizeof(RadioT));
	WidgetT *widget = NULL;

	if (!radio) return NULL;

	// We set the widget width and height in Init.
	widget = widgetInit(W(radio), MAGIC_RADIOBUTTON, x, y, 0, 0, 0, 0, 0, 0);
	if (!widget) {
		free(radio);
		return NULL;
	}

	radio = (RadioT *)radioInit((WidgetT *)radio, title, group);

	return radio;
}


static void radioPaint(WidgetT *widget, uint8_t enabled, RectT pos) {
	RadioT    *r = (RadioT *)widget;
	int16_t    i;
	int16_t    o;
	uint8_t    active;
	ColorT     highlight;
	ColorT     shadow;
	ColorT     fill;
	ColorT     text;
	ColorT     background;

	active     = (radioSelectedGet(r) == r);
	if (enabled) {
		highlight  = active ? _guiColor[COLOR_RADIOBUTTON_SHADOW] : _guiColor[COLOR_RADIOBUTTON_HIGHLIGHT];
		shadow     = active ? _guiColor[COLOR_RADIOBUTTON_HIGHLIGHT] : _guiColor[COLOR_RADIOBUTTON_SHADOW];
		fill       = active ? _guiColor[COLOR_RADIOBUTTON_ACTIVE] : _guiColor[COLOR_RADIOBUTTON_INACTIVE];
		text       = _guiColor[COLOR_CHECKBOX_TEXT];
		background = _guiColor[COLOR_WINDOW_BACKGROUND];
	} else {
		highlight  = active ? _guiColor[COLOR_RADIOBUTTON_SHADOW_DISABLED] : _guiColor[COLOR_RADIOBUTTON_HIGHLIGHT_DISABLED];
		shadow     = active ? _guiColor[COLOR_RADIOBUTTON_HIGHLIGHT_DISABLED] : _guiColor[COLOR_RADIOBUTTON_SHADOW_DISABLED];
		fill       = active ? _guiColor[COLOR_RADIOBUTTON_ACTIVE_DISABLED] : _guiColor[COLOR_RADIOBUTTON_INACTIVE_DISABLED];
		text       = _guiColor[COLOR_CHECKBOX_TEXT_DISABLED];
		background = _guiColor[COLOR_WINDOW_BACKGROUND];
	}

	// Radio button is 10x10 pixels.  Find offset based on font height.
	o = (_guiFont->height - 10) * 0.5;

	// Draw outline of radio button.
	surfaceLineDraw(pos.x,     pos.y + o + 5,  pos.x + 5,  pos.y + o,      highlight);
	surfaceLineDraw(pos.x + 5, pos.y + o,      pos.x + 10, pos.y + o + 5,  highlight);
	surfaceLineDraw(pos.x,     pos.y + o + 5,  pos.x + 5,  pos.y + o + 10, shadow);
	surfaceLineDraw(pos.x + 5, pos.y + o + 10, pos.x + 10, pos.y + o + 5,  shadow);

	// Fill radio button.
	for (i=0; i<4; i++) {
		surfaceLineDraw(pos.x + 5 - i, pos.y + o + i + 1, pos.x + 5 + i, pos.y + o + i + 1, fill);
		surfaceLineDraw(pos.x + 5 - i, pos.y + o - i + 8, pos.x + 5 + i, pos.y + o - i + 8, fill);
	}

	// Draw title.
	fontRender(_guiFont, r->title, text, background, pos.x + 10 + _guiMetric[METRIC_RADIOBUTTON_PADDING], pos.y);
}


static void radioSelectedInGroupClear(WidgetT *widget, uint32_t group) {
	size_t len = arrlenu(widget->children);
	size_t x;

	// Is this a Radio Button?
	if (widget->magic == MAGIC_RADIOBUTTON) {
		// Is this in our group and active?
		if (((RadioT *)widget)->group == group && GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE)) {
			// Deactivate it, mark it dirty, and stop searching.
			GUI_CLEAR_FLAG(widget, WIDGET_FLAG_ACTIVE);
			GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY);
			return;
		}
	}

	// Process any children.
	if (len > 0) {
		for (x=0; x<len; x++) {
			radioSelectedInGroupClear(widget->children[x], group);
		}
	}
}


static RadioT *radioSelectedInGroupFind(WidgetT *widget, uint32_t group) {
	size_t  len    = arrlenu(widget->children);
	size_t  x;
	RadioT *result = NULL;

	// Is this a Radio Button?
	if (widget->magic == MAGIC_RADIOBUTTON) {
		// Is this in our group and active?
		if (((RadioT *)widget)->group == group && GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE)) {
			// Found!  Stop searching.
			return (RadioT *)widget;
		}
	}

	// Process any children.
	if (len > 0) {
		for (x=0; x<len; x++) {
			result = radioSelectedInGroupFind(widget->children[x], group);
			if (result) break;
		}
	}

	return result;
}


RadioT *radioSelectedGet(RadioT *radio) {
	return radioSelectedInGroupFind(guiRootGet(), radio->group);
}


void radioSelectedSet(RadioT *radio) {
	// Are we already selected?
	if (!GUI_GET_FLAG((WidgetT *)radio, WIDGET_FLAG_ACTIVE)) {
		// Clear whoever is selected.
		radioSelectedInGroupClear(guiRootGet(), radio->group);
		// Select us.
		GUI_SET_FLAG((WidgetT *)radio, WIDGET_FLAG_ACTIVE);
		GUI_SET_FLAG((WidgetT *)radio, WIDGET_FLAG_DIRTY);
	}
}


void radioTitleSet(RadioT *radio, char *title) {
	if (radio->title) free(radio->title);
	radio->title = strdup(title);
	radio->base.pos.w = (strlen(title) * fontWidthGet(_guiFont)) + 10 + _guiMetric[METRIC_RADIOBUTTON_PADDING];
	GUI_SET_FLAG((WidgetT *)radio, WIDGET_FLAG_DIRTY);
}
