/*
 * mui_mui_slots.c
 *
 * Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
 *
 * SPDX-License-Identifier: MIT
 */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>

#include "mui.h"
#include "mii_mui_settings.h"


typedef struct mii_ui_machine_config_t {
	mui_window_t			win;

	mui_control_t * 		slot[8];

	// for sub-windows
	unsigned 				slot_id;
	mii_machine_config_t *	dst;
	mii_machine_config_t	config;
} mii_ui_machine_config_t;

static const mii_machine_config_t _default_config = {
	.no_slot_clock = 1,
	.titan_accelerator = 0,
	.slot = {
		[0] = {
//			.driver = MII_SLOT_DRIVER_SSC,
			.conf.ssc = {
				.kind = 0,
				.socket_port = 1969,
				.device = "/dev/ttyS0",
				.baud = 9600,
				.bits = 8,
				.parity = 0,
				.stop = 0,
			}
		},
		[1] = { .driver = 0, },
		[2] = { .driver = 0, },
		[3] = {
			.driver = MII_SLOT_DRIVER_MOUSE,
		},
		[4] = { .driver = 0, },
		[5] = {
			.driver = MII_SLOT_DRIVER_DISK2,
		},
		[6] = {
			.driver = MII_SLOT_DRIVER_SMARTPORT,
		},
	},
};

enum {
	MII_SLOT_WINDOW_ID 		= FCC('s','l','o','t'),
	MII_SLOT_SAVE 			= FCC('s','a','v','e'),
	MII_SLOT_CANCEL 		= FCC('c','a','n','c'),
	MII_SLOT_DEFAULT 		= FCC('d','e','f','a'),
	MII_SLOT_NSC 			= FCC('n','s','c','l'),
	MII_SLOT_TITAN 			= FCC('t','i','t','a'),
	MII_SLOT_DRIVER_POP 	= FCC('d','r','v','p'),
	MII_SLOT_DRIVER_CONF 	= FCC('d','r','v','c'),
};

static const struct {
	char * 	label;
	int		has_config;
} _mii_slot_drivers[] = {
	[MII_SLOT_DRIVER_NONE] 		= { "None", 0 },
	[MII_SLOT_DRIVER_SMARTPORT]	= { "SmartPort", 1 },
	[MII_SLOT_DRIVER_DISK2] 	= { "Disk II", 1 },
	[MII_SLOT_DRIVER_MOUSE] 	= { "Mouse", 0 },
	[MII_SLOT_DRIVER_SSC] 		= { "Super Serial", 1 },
	[MII_SLOT_DRIVER_ROM1MB]	= { "ROM 1MB", 1 },
//	[MII_SLOT_DRIVER_MOCKINGBOARD] = { "Mockingboard", },
#ifdef MII_DANII
	[MII_SLOT_DRIVER_DANII]		= { "DAN ][", 0 },
#endif
	{ NULL, 0 },
};

static void
_mii_config_win_to_conf(
		mii_ui_machine_config_t * m)
{
	mui_window_t * w = &m->win;
	mii_machine_config_t	* cf = &m->config;

	cf->titan_accelerator = !!mui_control_get_value(
					mui_control_get_by_id(w, MII_SLOT_TITAN));
	cf->no_slot_clock = !!mui_control_get_value(
					mui_control_get_by_id(w, MII_SLOT_NSC));

	for (int slot_id = 1; slot_id < 8; slot_id++) {
		mui_control_t * c = mui_control_get_by_id(
								w, MII_SLOT_DRIVER_POP + slot_id);
		mui_menu_items_t * items = mui_popupmenu_get_items(c);
		int pop_val = mui_control_get_value(c);
		mui_menu_item_t * item = &items->e[pop_val];
	//	printf("%s popup slot %d %4.4s '%s'\n", __func__,
	//			slot_id,
	//			(char*)&item->uid, item->title);
		mui_control_t * c2 = mui_control_get_by_id(w,
				MII_SLOT_DRIVER_CONF + slot_id);
		if (c2) {
			mui_control_set_state(c2,
						_mii_slot_drivers[pop_val].has_config ?
							MUI_CONTROL_STATE_NORMAL :
							MUI_CONTROL_STATE_DISABLED);
		}
		if (cf->slot[slot_id-1].driver != item->uid) {
			cf->slot[slot_id-1].driver = item->uid;
			memset(&cf->slot[slot_id-1].conf, 0,
					sizeof(cf->slot[slot_id-1].conf));
		}
	}
}

static void
_mii_config_conf_to_win(
		mii_ui_machine_config_t * m)
{
	mui_window_t * w = &m->win;
	mii_machine_config_t	* cf = &m->config;

//	mui_control_set_value(mui_control_get_by_id(w, MII_SLOT_REBOOT),
//					cf->reboot_on_load);
	mui_control_set_value(mui_control_get_by_id(w, MII_SLOT_TITAN),
					cf->titan_accelerator);
	mui_control_set_value(mui_control_get_by_id(w, MII_SLOT_NSC),
					cf->no_slot_clock);

	for (int slot_id = 1; slot_id < 8; slot_id++) {
		mui_control_t * c = mui_control_get_by_id(
								w, MII_SLOT_DRIVER_POP + slot_id);
		mui_control_set_value(c, cf->slot[slot_id-1].driver);

		mui_control_t * c2 = mui_control_get_by_id(w,
				MII_SLOT_DRIVER_CONF + slot_id);
		if (c2) {
			mui_control_set_state(c2,
						_mii_slot_drivers[cf->slot[slot_id-1].driver].has_config ?
							MUI_CONTROL_STATE_NORMAL :
							MUI_CONTROL_STATE_DISABLED);
		}
	}
}

static int
_mii_config_sub_save_cb(
		mui_window_t *win, // the sub window/dialog
		void * cb_param,
		uint32_t what,
		void * param)
{
//	mii_ui_machine_config_t * m = cb_param;
	printf("%s %4.4s\n", __func__, (char*)&what);

	return 0;
}

mui_window_t *
mii_mui_configure_slot(
		struct mui_t *mui,
		mii_machine_config_t *config,
		int slot_id)
{
	printf("%s config slot %d\n", __func__, slot_id);
	mui_window_t * res = NULL;
	switch (config->slot[slot_id].driver) {
		case MII_SLOT_DRIVER_SMARTPORT: {
			config->slot[slot_id].conf.smartport.slot_id = slot_id;
			res = mii_mui_load_2dsk(mui,
						&config->slot[slot_id].conf.smartport,
						MII_2DSK_SMARTPORT);
		}	break;
		case MII_SLOT_DRIVER_DISK2: {
			config->slot[slot_id].conf.disk2.slot_id = slot_id;
			res = mii_mui_load_2dsk(mui,
						&config->slot[slot_id].conf.disk2,
						MII_2DSK_DISKII);
		}	break;
		case MII_SLOT_DRIVER_ROM1MB: {
			config->slot[slot_id].conf.rom1mb.slot_id = slot_id;
			res = mii_mui_load_1mbrom(mui,
						&config->slot[slot_id].conf.rom1mb);
		}	break;
		case MII_SLOT_DRIVER_SSC: {
			config->slot[slot_id].conf.ssc.slot_id = slot_id;
			res = mii_mui_configure_ssc(mui,
						&config->slot[slot_id].conf.ssc);
		}	break;
	}
	return res;
}

static int
_mii_config_slot_action_cb(
		mui_control_t * c,	// control here
		void * 			cb_param, // mii_ui_machine_config_t
		uint32_t 		what,
		void * 			param)	// not used
{
	printf("%s %4.4s\n", __func__, (char*)&what);
	mii_ui_machine_config_t * m = cb_param;
	uint32_t uid = mui_control_get_uid(c);

	switch (what) {
		case MUI_CONTROL_ACTION_SELECT:
			printf("%s control %4.4s\n", __func__, (char*)&uid);
			switch (uid) {
				case MII_SLOT_SAVE: {
					// save the config
					printf("%s save\n", __func__);
					_mii_config_win_to_conf(m);
					if (m->dst)
						*m->dst = m->config;
					mui_window_action(&m->win, MII_MUI_SLOTS_SAVE, m->dst);
					mui_window_dispose(&m->win);
				}	break;
				case MII_SLOT_CANCEL: {
					// cancel the config
					printf("%s cancel\n", __func__);
					mui_window_dispose(&m->win);
				}	break;
				case MII_SLOT_DEFAULT: {
					// set the default config
					printf("%s default\n", __func__);
					m->config = _default_config;
					_mii_config_conf_to_win(m);
				}	break;
#if 0
				case MII_SLOT_REBOOT: {
					// reboot the machine
					printf("%s reboot\n", __func__);
					m->config.reboot_on_load = !m->config.reboot_on_load;
				}	break;
#endif
				case MII_SLOT_NSC: {
					// no slot clock
					printf("%s nsc\n", __func__);
					m->config.no_slot_clock = !m->config.no_slot_clock;
				}	break;
				case MII_SLOT_TITAN: {
					// titan accelerator
					printf("%s titan\n", __func__);
					m->config.titan_accelerator = !m->config.titan_accelerator;
				}	break;
				case MII_SLOT_DRIVER_CONF + 1 ... MII_SLOT_DRIVER_CONF + 7: {
					// configure a slot
					int slot_id = uid - MII_SLOT_DRIVER_CONF - 1;
					printf("%s config slot %d\n", __func__, slot_id);
					switch (m->config.slot[slot_id].driver) {
						case MII_SLOT_DRIVER_SMARTPORT: {
							m->config.slot[slot_id].conf.smartport.slot_id = slot_id;
							mui_window_set_action(
								mii_mui_load_2dsk(m->win.ui,
										&m->config.slot[slot_id].conf.smartport,
										MII_2DSK_SMARTPORT),
								_mii_config_sub_save_cb, m);
						}	break;
						case MII_SLOT_DRIVER_DISK2: {
							m->config.slot[slot_id].conf.disk2.slot_id = slot_id;
							mui_window_set_action(
								mii_mui_load_2dsk(m->win.ui,
										&m->config.slot[slot_id].conf.disk2,
										MII_2DSK_DISKII),
								_mii_config_sub_save_cb, m);
						}	break;
						case MII_SLOT_DRIVER_ROM1MB: {
							m->config.slot[slot_id].conf.rom1mb.slot_id = slot_id;
							mui_window_set_action(
								mii_mui_load_1mbrom(m->win.ui,
										&m->config.slot[slot_id].conf.rom1mb),
								_mii_config_sub_save_cb, m);
						}	break;
						case MII_SLOT_DRIVER_SSC: {
							m->config.slot[slot_id].conf.ssc.slot_id = slot_id;
							mui_window_set_action(
								mii_mui_configure_ssc(m->win.ui,
										&m->config.slot[slot_id].conf.ssc),
								_mii_config_sub_save_cb, m);
						}	break;
					}
				}	break;
			}
			break;
		case MUI_CONTROL_ACTION_VALUE_CHANGED:
			// a popup menu changed
			printf("%s popup %4.4s\n", __func__, (char*)&uid);
			switch (uid) {
				case MII_SLOT_DRIVER_POP + 1 ... MII_SLOT_DRIVER_POP + 7: {
					_mii_config_win_to_conf(m);
				}	break;
			}
			break;
	}
	return 0;
}

struct mui_window_t *
mii_mui_configure_slots(
		struct mui_t *mui,
		mii_machine_config_t *config)
{
	mui_t *ui = mui;
	float base_size = mui_font_find(ui, "main")->size;
	float margin = base_size * 0.7;

	mui_window_t *w = mui_window_get_by_id(mui, MII_SLOT_WINDOW_ID);
	if (w) {
		mui_window_select(w);
		return w;
	}
	c2_pt_t where = {};
	c2_rect_t wpos = C2_RECT_WH(where.x, where.y, 520, 480);
	if (where.x == 0 && where.y == 0)
		c2_rect_offset(&wpos,
			(ui->screen_size.x / 2) - (c2_rect_width(&wpos) / 2),
			(ui->screen_size.y * 0.4) - (c2_rect_height(&wpos) / 2));
	w = mui_window_create(mui,
					wpos,
					NULL, 0,
					"Configure " MUI_GLYPH_IIE " Slots",
					sizeof(mii_ui_machine_config_t));
	mui_window_set_id(w, MII_SLOT_WINDOW_ID);
	mii_ui_machine_config_t *m = (mii_ui_machine_config_t*)w;

	mui_control_t * c = NULL;
	c2_rect_t cf;
	cf = C2_RECT_WH(0, 0, 120, 40);
	c2_rect_left_of(&cf, c2_rect_width(&w->content), 20);
	c2_rect_top_of(&cf, c2_rect_height(&w->content), 20);
	c = mui_button_new(w,
					cf, MUI_BUTTON_STYLE_DEFAULT,
					"Reboot", MII_SLOT_SAVE);
	c->key_equ = MUI_KEY_EQU(0, 13);
	c2_rect_left_of(&cf, cf.l, 20);
	c = mui_button_new(w,
					cf, MUI_BUTTON_STYLE_NORMAL,
					"Cancel", MII_SLOT_CANCEL);
	c->key_equ = MUI_KEY_EQU(0, 27);

	c2_rect_right_of(&cf, 0, 20);
	cf.r = cf.l + 200;
	#if 0
	c = mui_button_new(w,
					cf, MUI_BUTTON_STYLE_CHECKBOX,
					"Reboot Now", MII_SLOT_REBOOT);
	#endif
	c2_rect_top_of(&cf, cf.t, 36);
	cf.r = cf.l + 120;
	c = mui_button_new(w,
					cf, MUI_BUTTON_STYLE_NORMAL,
					"Default", MII_SLOT_DEFAULT);
	c2_rect_right_of(&cf, cf.r, 30);
	c2_rect_t rad = cf;
	rad.b += 5;
	c = mui_button_new(w,
					rad, MUI_BUTTON_STYLE_CHECKBOX,
					"No Slot Clock", MII_SLOT_NSC);
	c2_rect_right_of(&rad, rad.r, margin);
	rad.r = rad.l + 180;
	c = mui_button_new(w,
					rad, MUI_BUTTON_STYLE_CHECKBOX,
					"Titan Accelerator", MII_SLOT_TITAN);

	c2_rect_bottom_of(&cf, cf.b, 16);
	cf.l = 60;
	cf.r = c2_rect_width(&w->content) - 60;
	cf.b = cf.t + 1;
	c = mui_separator_new(w, cf);

	c2_rect_t slot_line_rect = C2_RECT_WH(0, 0, 500, 34);
	cf = slot_line_rect;
	cf.r = cf.l + 56;
	c = mui_textbox_new(w, cf, "Slot", NULL,
				MUI_TEXT_ALIGN_RIGHT | MUI_TEXT_ALIGN_MIDDLE |
				MUI_TEXT_STYLE_ULINE);
	c2_rect_right_of(&cf, cf.r, 0);
	cf.r = cf.l + 240;
	c = mui_textbox_new(w, cf, "Driver", NULL,
				MUI_TEXT_ALIGN_CENTER | MUI_TEXT_ALIGN_MIDDLE |
				MUI_TEXT_STYLE_ULINE);
	c2_rect_right_of(&cf, cf.r, 30);
	cf.r = cf.l + 150;
	c = mui_textbox_new(w, cf, "Config", NULL,
				MUI_TEXT_ALIGN_CENTER | MUI_TEXT_ALIGN_MIDDLE |
				MUI_TEXT_STYLE_ULINE);

	c2_rect_offset(&slot_line_rect, 0, 36);
	for (int i = 1; i < 8; i++) {
		cf = slot_line_rect;
		cf.r = cf.l + 50;
		char idx[16];
		sprintf(idx, "%d:", i);
		c = mui_textbox_new(w, cf, idx, NULL,
				MUI_TEXT_ALIGN_RIGHT | MUI_TEXT_ALIGN_MIDDLE |
				MUI_TEXT_STYLE_ULINE);
		c2_rect_right_of(&cf, cf.r, 6);
		cf.r = cf.l + 240;
		c = mui_popupmenu_new(w, cf,
						"Popup", MII_SLOT_DRIVER_POP + i,
						MUI_TEXT_ALIGN_CENTER);
		mui_menu_items_t * items = mui_popupmenu_get_items(c);
		mui_menu_items_clear(items);
		for (int j = 0; _mii_slot_drivers[j].label; j++) {
			mui_menu_item_t i = {
//				.title = strdup(_mii_slot_drivers[j].label),
				.title = _mii_slot_drivers[j].label,
				.uid = j,
			};
			mui_menu_items_push(items, i);
		}
		mui_menu_item_t z = {};
		mui_menu_items_push(items, z);
		mui_popupmenu_prepare(c);

		c2_rect_right_of(&cf, cf.r, 30);
		cf.r = cf.l + 150;
		c = mui_button_new(w,
						cf, MUI_BUTTON_STYLE_NORMAL,
						"Config…", MII_SLOT_DRIVER_CONF + i);
		c2_rect_offset(&slot_line_rect, 0, 38);
	}
	c = NULL;
	TAILQ_FOREACH(c, &w->controls, self) {
		if (mui_control_get_uid(c) == 0)
			continue;
		mui_control_set_action(c, _mii_config_slot_action_cb, m);
	}
	m->dst = config;
	if (config) {
		if (config->load_defaults)
			m->config = _default_config;
		else
			m->config = *config;
	}
	_mii_config_conf_to_win(m);
	return w;
}
