/**
 * @brief Panel window list widget
 */
#include <toaru/yutani.h>
#include <toaru/graphics.h>
#include <toaru/menu.h>
#include <toaru/text.h>
#include <toaru/panel.h>
#include <toaru/icon_cache.h>

#define GRADIENT_HEIGHT (PANEL_HEIGHT-2*Y_PAD)
#define GRADIENT_AT(y) premultiply(rgba(255,72,254,220-y*7))
#define MAX_TEXT_WIDTH 180
#define MIN_TEXT_WIDTH 50
#define TOTAL_CELL_WIDTH (title_width)
static struct MenuList * window_menu;
static int title_width = 0;
static yutani_wid_t _window_menu_wid = 0;

/* Update the hover-focus window */
static void set_focused(int i) {
	if (focused_app != i) {
		focused_app = i;
		redraw();
	}
}

static void _window_menu_start_move(struct MenuEntry * self) {
	if (!_window_menu_wid)
		return;
	yutani_focus_window(yctx, _window_menu_wid);
	yutani_window_drag_start_wid(yctx, _window_menu_wid);
}

static void _window_menu_start_maximize(struct MenuEntry * self) {
	if (!_window_menu_wid)
		return;
	yutani_special_request_wid(yctx, _window_menu_wid, YUTANI_SPECIAL_REQUEST_MAXIMIZE);
	yutani_focus_window(yctx, _window_menu_wid);
}

static void _window_menu_close(struct MenuEntry * self) {
	if (!_window_menu_wid)
		return;
	yutani_focus_window(yctx, _window_menu_wid);
	yutani_special_request_wid(yctx, _window_menu_wid, YUTANI_SPECIAL_REQUEST_PLEASE_CLOSE);
}

static void window_show_menu(yutani_wid_t wid, int x) {
	if (window_menu->window) return;
	_window_menu_wid = wid;
	panel_menu_show_at(window_menu, x);
}

static int widget_draw_windowlist(struct PanelWidget * this, gfx_context_t * ctx) {
	if (!window_list || window_list->length == 0) {
		title_width = 0;
	} else if (this->width <= 0) {
		title_width = 28;
	} else {
		title_width = this->width / window_list->length;
		if (title_width > MAX_TEXT_WIDTH) {
			title_width = MAX_TEXT_WIDTH;
		}
		if (title_width < MIN_TEXT_WIDTH) {
			title_width = 28;
		}
	}

	int i = 0, j = 0;
	if (window_list) {
		foreach(node, window_list) {
			struct window_ad * ad = node->value;
			int w = title_width;

			if (i + w > this->width) {
				break;
			}

			/* Hilight the focused window */
			if (ad->flags & 1) {
				/* This is the focused window */
				for (int y = 0; y < GRADIENT_HEIGHT; ++y) {
					for (int x = i; x < i + w; ++x) {
						GFX(ctx, x, y) = alpha_blend_rgba(GFX(ctx, x, y), GRADIENT_AT(y));
					}
				}
			}

			if (title_width >= MIN_TEXT_WIDTH) {
				/* Ellipsifiy the title */
				char * s = ellipsify(ad->name, 14, font, title_width - 4, NULL);
				sprite_t * icon = icon_get_48(ad->icon);
				gfx_context_t * subctx = init_graphics_subregion(ctx, i, 0, w, ctx->height);
				draw_sprite_scaled_alpha(subctx, icon, w - 48 - 2, 0, 48, 48, (ad->flags & 1) ? 1.0 : 0.7);
				tt_draw_string_shadow(subctx, font, s, 14, 2, TEXT_Y_OFFSET, (j == focused_app) ? HILIGHT_COLOR : (ad->flags & 1) ? FOCUS_COLOR : TEXT_COLOR, rgb(0,0,0), 4);
				free(subctx);
				free(s);
			} else {
				sprite_t * icon = icon_get_16(ad->icon);
				gfx_context_t * subctx = init_graphics_subregion(ctx, i, 0, w, ctx->height);
				draw_sprite_scaled(subctx, icon, 6, 6, 16, 16);
				free(subctx);
			}

			ad->left = this->left + i;

			j++;
			i += w;
		}
	}

	return 0;
}

static int widget_click_windowlist(struct PanelWidget * this, struct yutani_msg_window_mouse_event * evt) {
	foreach(node, window_list) {
		struct window_ad * ad = node->value;
		if (evt->new_x >= ad->left && evt->new_x < ad->left + TOTAL_CELL_WIDTH) {
			yutani_focus_window(yctx, ad->wid);
			break;
		}
	}
	return 0;
}

static int widget_rightclick_windowlist(struct PanelWidget * this, struct yutani_msg_window_mouse_event * evt) {
	foreach(node, window_list) {
		struct window_ad * ad = node->value;
		if (evt->new_x >= ad->left && evt->new_x < ad->left + TOTAL_CELL_WIDTH) {
			window_show_menu(ad->wid, evt->new_x);
		}
	}
	return 0;
}


static int widget_move_windowlist(struct PanelWidget * this, struct yutani_msg_window_mouse_event * evt) {
	int found = 0;
	int i = 0;

	foreach(node, window_list) {
		struct window_ad * ad = node->value;
		if (evt->new_x >= ad->left && evt->new_x < ad->left + TOTAL_CELL_WIDTH) {
			found = 1;
			set_focused(i);
			break;
		}
		i++;
	}

	if (!found) {
		set_focused(-1);
	}

	int scroll_direction = 0;
	if (evt->buttons & YUTANI_MOUSE_SCROLL_UP) scroll_direction = -1;
	else if (evt->buttons & YUTANI_MOUSE_SCROLL_DOWN) scroll_direction = 1;

	if (scroll_direction != 0) {
		struct window_ad * last = window_list->tail ? window_list->tail->value : NULL;
		int focus_next = 0;
		foreach(node, window_list) {
			struct window_ad * ad = node->value;
			if (focus_next) {
				yutani_focus_window(yctx, ad->wid);
				return 1;
			}
			if (ad->flags & 1) {
				if (scroll_direction == -1) {
					yutani_focus_window(yctx, last->wid);
					return 1;
				}
				if (scroll_direction == 1) {
					focus_next = 1;
				}
			}
			last = ad;
		}
		if (focus_next && window_list->head) {
			struct window_ad * ad = window_list->head->value;
			yutani_focus_window(yctx, ad->wid);
			return 1;
		}
	}

	return 1;
}

static int widget_leave_windowlist(struct PanelWidget * this, struct yutani_msg_window_mouse_event * evt) {
	this->highlighted = 0;
	set_focused(-1);
	return 1;
}

static int widget_onkey_windowlist(struct PanelWidget * this, struct yutani_msg_key_event * ke) {
	if ((ke->event.modifiers & KEY_MOD_LEFT_ALT) &&
		(ke->event.keycode == KEY_F3) &&
		(ke->event.action == KEY_ACTION_DOWN)) {
		foreach(node, window_list) {
			struct window_ad * ad = node->value;
			if (ad->flags & 1) {
				window_show_menu(ad->wid, ad->left + title_width / 2);
			}
		}
	}
	return 0;
}

struct PanelWidget * widget_init_windowlist(void) {
	window_menu = menu_create();
	window_menu->flags |= MENU_FLAG_BUBBLE_LEFT;
	menu_insert(window_menu, menu_create_normal(NULL, NULL, "Maximize", _window_menu_start_maximize));
	menu_insert(window_menu, menu_create_normal(NULL, NULL, "Move", _window_menu_start_move));
	menu_insert(window_menu, menu_create_separator());
	menu_insert(window_menu, menu_create_normal(NULL, NULL, "Close", _window_menu_close));

	/* Alt+F3 = window context menu */
	yutani_key_bind(yctx, KEY_F3, KEY_MOD_LEFT_ALT, YUTANI_BIND_STEAL);

	struct PanelWidget * widget = widget_new();
	widget->fill = 1;
	widget->draw = widget_draw_windowlist;
	widget->click = widget_click_windowlist;
	widget->right_click = widget_rightclick_windowlist;
	widget->move = widget_move_windowlist;
	widget->leave = widget_leave_windowlist;
	widget->onkey = widget_onkey_windowlist;
	list_insert(widgets_enabled, widget);
	return widget;
}

