/**
 * @brief Panel Volume Widget
 *
 * Shows an icon indicating the mixer's master volume,
 * and shows a menu with a volume slider when clicked.
 */
#include <fcntl.h>
#include <sys/ioctl.h>
#include <kernel/mod/sound.h>

#include <toaru/yutani.h>
#include <toaru/graphics.h>
#include <toaru/menu.h>
#include <toaru/text.h>
#include <toaru/panel.h>

#define VOLUME_DEVICE_ID 0
#define VOLUME_KNOB_ID   0

static sprite_t * sprite_volume_mute;
static sprite_t * sprite_volume_low;
static sprite_t * sprite_volume_med;
static sprite_t * sprite_volume_high;
static struct MenuList * volume_menu;

static long volume_level = 0;
static int mixer = -1;

static int widget_update_volume(struct PanelWidget * this) {
	if (mixer == -1) {
		mixer = open("/dev/mixer", O_RDONLY);
	}

	snd_knob_value_t value = {0};
	value.device = VOLUME_DEVICE_ID; /* TODO configure this somewhere */
	value.id     = VOLUME_KNOB_ID;   /* TODO this too */

	ioctl(mixer, SND_MIXER_READ_KNOB, &value);
	volume_level = value.val;

	return 0;
}

static void set_volume(void) {
	snd_knob_value_t value = {0};
	value.device = VOLUME_DEVICE_ID; /* TODO configure this somewhere */
	value.id     = VOLUME_KNOB_ID;   /* TODO this too */
	value.val    = volume_level;

	ioctl(mixer, SND_MIXER_WRITE_KNOB, &value);
	redraw();
}

static void volume_raise(void) {
	volume_level += 0x10000000;
	if (volume_level > 0xF0000000) volume_level = 0xFC000000;
	set_volume();
}

static void volume_lower(void) {
	volume_level -= 0x10000000;
	if (volume_level < 0x0) volume_level = 0x0;
	set_volume();
}

#define VOLUME_SLIDER_LEFT_PAD  38
#define VOLUME_SLIDER_RIGHT_PAD  14
#define VOLUME_SLIDER_PAD (VOLUME_SLIDER_LEFT_PAD + VOLUME_SLIDER_RIGHT_PAD)
#define VOLUME_SLIDER_VERT_PAD   10
#define VOLUME_SLIDER_BALL_RADIUS 8

struct SliderStuff {
	int level;
	uint32_t on;
	uint32_t off;
};

uint32_t volume_pattern(int32_t x, int32_t y, double alpha, void * extra) {
	struct SliderStuff * stuff = extra;
	if (alpha > 1.0) alpha = 1.0;
	if (alpha < 0.0) alpha = 0.0;
	uint32_t color = stuff->off;
	if (x < stuff->level + VOLUME_SLIDER_LEFT_PAD) {
		color = stuff->on;
	}
	color |= rgba(0,0,0,alpha*255);
	return premultiply(color);
}

void _menu_draw_MenuEntry_Slider(gfx_context_t * ctx, struct MenuEntry * self, int offset) {
	self->offset = offset;

	draw_sprite_alpha_paint(ctx, sprite_volume_high, 4, offset, 1.0, rgb(0,0,0));

	struct SliderStuff stuff;
	stuff.level = (ctx->width - VOLUME_SLIDER_PAD) * (float)volume_level / (float)0xFC000000;
	stuff.on  = rgba(210,50,210,0);
	stuff.off = rgba(140,140,140,0);
	draw_rounded_rectangle_pattern(ctx,
		/* x */ VOLUME_SLIDER_LEFT_PAD - 4,
		/* y */ offset + VOLUME_SLIDER_VERT_PAD - 1,
		/* w */ ctx->width - VOLUME_SLIDER_PAD + 8,
		/* h */ self->height - 2 * VOLUME_SLIDER_VERT_PAD + 2, 6, volume_pattern, &stuff);
	stuff.on =  rgba(255,72,254,0);
	stuff.off = rgba(200,200,200,0);
	draw_rounded_rectangle_pattern(ctx,
		/* x */ VOLUME_SLIDER_LEFT_PAD - 3,
		/* y */ offset + VOLUME_SLIDER_VERT_PAD,
		/* w */ ctx->width - VOLUME_SLIDER_PAD + 6,
		/* h */ self->height - 2 * VOLUME_SLIDER_VERT_PAD, 5, volume_pattern, &stuff);

	draw_rounded_rectangle(ctx,
		/* x */ stuff.level - VOLUME_SLIDER_BALL_RADIUS + VOLUME_SLIDER_LEFT_PAD,
		/* y */ offset + 12 - VOLUME_SLIDER_BALL_RADIUS,
		/* w */ VOLUME_SLIDER_BALL_RADIUS * 2,
		/* h */ VOLUME_SLIDER_BALL_RADIUS * 2, VOLUME_SLIDER_BALL_RADIUS, rgb(140,140,140));
	draw_rounded_rectangle(ctx,
		/* x */ stuff.level - VOLUME_SLIDER_BALL_RADIUS + 1 + VOLUME_SLIDER_LEFT_PAD,
		/* y */ offset + 12 - VOLUME_SLIDER_BALL_RADIUS + 1,
		/* w */ VOLUME_SLIDER_BALL_RADIUS * 2 - 2,
		/* h */ VOLUME_SLIDER_BALL_RADIUS * 2 - 2, VOLUME_SLIDER_BALL_RADIUS - 1, rgb(220,220,220));
}

int _menu_mouse_MenuEntry_Slider(struct MenuEntry * self, struct yutani_msg_window_mouse_event * event) {
	if (event->buttons & YUTANI_MOUSE_BUTTON_LEFT) {
		/* Figure out where it is */
		float level = (float)(event->new_x - VOLUME_SLIDER_LEFT_PAD) / (float)(self->width - VOLUME_SLIDER_PAD);
		if (level >= 1.0) level = 1.0;
		if (level <= 0.0) level = 0.0;
		if (volume_level != level * 0xFC000000) {
			volume_level = level * 0xFC000000;
			set_volume();
			return 1;
		}
	}
	return 0;
}

static struct MenuEntryVTable slider_vtable = {
	.methods = 4,
	.renderer = _menu_draw_MenuEntry_Slider,
	.mouse_event = _menu_mouse_MenuEntry_Slider,
};

struct MenuEntry * menu_create_slider(void) {
	struct MenuEntry * out = menu_create_separator(); /* Steal some defaults */
	out->_type = -1; /* Special */
	out->height = 24;
	out->rwidth = 200;
	out->vtable = &slider_vtable;
	return out;
}


static int widget_click_volume(struct PanelWidget * this, struct yutani_msg_window_mouse_event * evt) {
	if (!volume_menu) {
		volume_menu = menu_create();
		volume_menu->flags |= MENU_FLAG_BUBBLE_LEFT;
	}

	/* Clear the menu */
	while (volume_menu->entries->length) {
		node_t * node = list_pop(volume_menu->entries);
		menu_free_entry((struct MenuEntry *)node->value);
		free(node);
	}

	menu_insert(volume_menu, menu_create_slider());

	/* TODO Our mixer supports multiple knobs and we could show all of them. */
	/* TODO We could also show a nice slider... if we had one... */

	if (!volume_menu->window) {
		panel_menu_show(this, volume_menu);
	}

	return 1;
}


static int widget_draw_volume(struct PanelWidget * this, gfx_context_t * ctx) {
	uint32_t color = (volume_menu && volume_menu->window) ? HILIGHT_COLOR : ICON_COLOR;
	if (volume_level < 10) {
		draw_sprite_alpha_paint(ctx, sprite_volume_mute, 0, 1, 1.0, color);
	} else if (volume_level < 0x547ae147) {
		draw_sprite_alpha_paint(ctx, sprite_volume_low, 0, 1, 1.0, color);
	} else if (volume_level < 0xa8f5c28e) {
		draw_sprite_alpha_paint(ctx, sprite_volume_med, 0, 1, 1.0, color);
	} else {
		draw_sprite_alpha_paint(ctx, sprite_volume_high, 0, 1, 1.0, color);
	}

	return 0;
}

/* For dumb legacy reasons, scroll wheel movement shows up here... */
static int widget_move_volume(struct PanelWidget * this, struct yutani_msg_window_mouse_event * evt) {
	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 == 1) {
		volume_lower();
		return 1;
	} else if (scroll_direction == -1) {
		volume_raise();
		return 1;
	}

	return 0;
}

struct PanelWidget * widget_init_volume(void) {
	sprite_volume_mute = malloc(sizeof(sprite_t));
	sprite_volume_low  = malloc(sizeof(sprite_t));
	sprite_volume_med  = malloc(sizeof(sprite_t));
	sprite_volume_high = malloc(sizeof(sprite_t));
	load_sprite(sprite_volume_mute, "/usr/share/icons/24/volume-mute.png");
	load_sprite(sprite_volume_low,  "/usr/share/icons/24/volume-low.png");
	load_sprite(sprite_volume_med,  "/usr/share/icons/24/volume-medium.png");
	load_sprite(sprite_volume_high, "/usr/share/icons/24/volume-full.png");

	struct PanelWidget * widget = widget_new();
	widget->width = 24;
	widget->draw = widget_draw_volume;
	widget->click = widget_click_volume;
	widget->move  = widget_move_volume;
	widget->update = widget_update_volume;
	list_insert(widgets_enabled, widget);
	return widget;
}

