/* Copyright holders: Sarah Walker
   see COPYING for more details
*/
#include <windows.h>
#include <windowsx.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <wchar.h>
#include <86box/86box.h>
#include <86box/config.h>
#include <86box/device.h>
#include <86box/gameport.h>
#include <86box/plat.h>
#include <86box/win.h>


static int joystick_nr;
static int joystick_config_type;
#define AXIS_STRINGS_MAX 3
static char *axis_strings[AXIS_STRINGS_MAX] = {"X Axis", "Y Axis", "Z Axis"};

static uint8_t joystickconfig_changed = 0;


static void rebuild_axis_button_selections(HWND hdlg)
{
        int id = IDC_CONFIG_BASE + 2;
        HWND h;
        int joystick;
        int c, d;
	char s[269];

        h = GetDlgItem(hdlg, IDC_CONFIG_BASE);
        joystick = SendMessage(h, CB_GETCURSEL, 0, 0);

        for (c = 0; c < joystick_get_axis_count(joystick_config_type); c++)
        {
                int sel = c;

                h = GetDlgItem(hdlg, id);
                SendMessage(h, CB_RESETCONTENT, 0, 0);

                if (joystick)
                {
                        for (d = 0; d < plat_joystick_state[joystick-1].nr_axes; d++)
                        {
                                SendMessage(h, CB_ADDSTRING, 0, (LPARAM)(LPCSTR)plat_joystick_state[joystick-1].axis[d].name);
                                if (c < AXIS_STRINGS_MAX)
                                {
                                        if (!stricmp(axis_strings[c], plat_joystick_state[joystick-1].axis[d].name))
                                                sel = d;
                                }
                        }
                        for (d = 0; d < plat_joystick_state[joystick-1].nr_povs; d++)
                        {
                                sprintf(s, "%s (X axis)", plat_joystick_state[joystick-1].pov[d].name);
                                SendMessage(h, CB_ADDSTRING, 0, (LPARAM)(LPCSTR)s);
                                sprintf(s, "%s (Y axis)", plat_joystick_state[joystick-1].pov[d].name);
                                SendMessage(h, CB_ADDSTRING, 0, (LPARAM)(LPCSTR)s);
                        }
			for (d = 0; d < plat_joystick_state[joystick - 1].nr_sliders; d++)
                        {
                                SendMessage(h, CB_ADDSTRING, 0, (LPARAM)(LPCSTR)plat_joystick_state[joystick - 1].slider[d].name);
                        }
                        SendMessage(h, CB_SETCURSEL, sel, 0);
                        EnableWindow(h, TRUE);
                }
                else
                        EnableWindow(h, FALSE);

                id += 2;
        }

        for (c = 0; c < joystick_get_button_count(joystick_config_type); c++)
        {
                h = GetDlgItem(hdlg, id);
                SendMessage(h, CB_RESETCONTENT, 0, 0);

                if (joystick)
                {
                        for (d = 0; d < plat_joystick_state[joystick-1].nr_buttons; d++)
                                SendMessage(h, CB_ADDSTRING, 0, (LPARAM)(LPCSTR)plat_joystick_state[joystick-1].button[d].name);
                        SendMessage(h, CB_SETCURSEL, c, 0);
                        EnableWindow(h, TRUE);
                }
                else
                        EnableWindow(h, FALSE);

                id += 2;
        }

        for (c = 0; c < joystick_get_pov_count(joystick_config_type)*2; c++)
        {
                int sel = c;

                h = GetDlgItem(hdlg, id);
                SendMessage(h, CB_RESETCONTENT, 0, 0);

                if (joystick)
                {
                        for (d = 0; d < plat_joystick_state[joystick-1].nr_povs; d++)
                        {
                                sprintf(s, "%s (X axis)", plat_joystick_state[joystick-1].pov[d].name);
                                SendMessage(h, CB_ADDSTRING, 0, (LPARAM)(LPCSTR)s);
                                sprintf(s, "%s (Y axis)", plat_joystick_state[joystick-1].pov[d].name);
                                SendMessage(h, CB_ADDSTRING, 0, (LPARAM)(LPCSTR)s);
                        }
                        for (d = 0; d < plat_joystick_state[joystick-1].nr_axes; d++)
                        {
                                SendMessage(h, CB_ADDSTRING, 0, (LPARAM)(LPCSTR)plat_joystick_state[joystick-1].axis[d].name);
                        }
                        SendMessage(h, CB_SETCURSEL, sel, 0);
                        EnableWindow(h, TRUE);
                }
                else
                        EnableWindow(h, FALSE);

                id += 2;
        }

}

static int get_axis(HWND hdlg, int id)
{
        HWND h = GetDlgItem(hdlg, id);
        int axis_sel = SendMessage(h, CB_GETCURSEL, 0, 0);
        int nr_axes = plat_joystick_state[joystick_state[joystick_nr].plat_joystick_nr-1].nr_axes;
	int nr_povs = plat_joystick_state[joystick_state[joystick_nr].plat_joystick_nr - 1].nr_povs;

        if (axis_sel < nr_axes)
                return axis_sel;

        axis_sel -= nr_axes;
        if (axis_sel < nr_povs * 2)
        {
                if (axis_sel & 1)
                        return POV_Y | (axis_sel >> 1);
                else
                        return POV_X | (axis_sel >> 1);
        }
        axis_sel -= nr_povs;

        return SLIDER | (axis_sel >> 1);
}

static int get_pov(HWND hdlg, int id)
{
        HWND h = GetDlgItem(hdlg, id);
        int axis_sel = SendMessage(h, CB_GETCURSEL, 0, 0);
        int nr_povs = plat_joystick_state[joystick_state[joystick_nr].plat_joystick_nr-1].nr_povs*2;

        if (axis_sel < nr_povs)
        {
                if (axis_sel & 1)
                        return POV_Y | (axis_sel >> 1);
                else
                        return POV_X | (axis_sel >> 1);
        }

        return axis_sel - nr_povs;
}

#if defined(__amd64__) || defined(__aarch64__)
static LRESULT CALLBACK
#else
static BOOL CALLBACK
#endif
joystickconfig_dlgproc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	HWND h;
	int c;
	int id;
	int joystick;
	int nr_axes;
	int nr_povs;
	int mapping;

        switch (message)
        {
                case WM_INITDIALOG:
                {
                        h = GetDlgItem(hdlg, IDC_CONFIG_BASE);
                        id = IDC_CONFIG_BASE + 2;
                        joystick = joystick_state[joystick_nr].plat_joystick_nr;

                        SendMessage(h, CB_ADDSTRING, 0, (LPARAM)(LPCSTR)"None");

                        for (c = 0; c < joysticks_present; c++)
                                SendMessage(h, CB_ADDSTRING, 0, (LPARAM)(LPCSTR)plat_joystick_state[c].name);

                        SendMessage(h, CB_SETCURSEL, joystick, 0);

                        rebuild_axis_button_selections(hdlg);

                        if (joystick_state[joystick_nr].plat_joystick_nr)
                        {
                                nr_axes = plat_joystick_state[joystick-1].nr_axes;
                                nr_povs = plat_joystick_state[joystick-1].nr_povs;

                                for (c = 0; c < joystick_get_axis_count(joystick_config_type); c++)
                                {
                                        int mapping = joystick_state[joystick_nr].axis_mapping[c];

                                        h = GetDlgItem(hdlg, id);
                                        if (mapping & POV_X)
                                                SendMessage(h, CB_SETCURSEL, nr_axes + (mapping & 3)*2, 0);
                                        else if (mapping & POV_Y)
                                                SendMessage(h, CB_SETCURSEL, nr_axes + (mapping & 3)*2 + 1, 0);
                                        else if (mapping & SLIDER)
						SendMessage(h, CB_SETCURSEL, nr_axes + nr_povs * 2 + (mapping & 3), 0);
					else
                                                SendMessage(h, CB_SETCURSEL, mapping, 0);
                                        id += 2;
                                }
                                for (c = 0; c < joystick_get_button_count(joystick_config_type); c++)
                                {
                                        h = GetDlgItem(hdlg, id);
                                        SendMessage(h, CB_SETCURSEL, joystick_state[joystick_nr].button_mapping[c], 0);
                                        id += 2;
                                }
                                for (c = 0; c < joystick_get_pov_count(joystick_config_type); c++)
                                {
                                        h = GetDlgItem(hdlg, id);
                                        mapping = joystick_state[joystick_nr].pov_mapping[c][0];
                                        if (mapping & POV_X)
                                                SendMessage(h, CB_SETCURSEL, (mapping & 3)*2, 0);
                                        else if (mapping & POV_Y)
                                                SendMessage(h, CB_SETCURSEL, (mapping & 3)*2 + 1, 0);
                                        else
                                                SendMessage(h, CB_SETCURSEL, mapping + nr_povs*2, 0);
                                        id += 2;
                                        h = GetDlgItem(hdlg, id);
                                        mapping = joystick_state[joystick_nr].pov_mapping[c][1];
                                        if (mapping & POV_X)
                                                SendMessage(h, CB_SETCURSEL, (mapping & 3)*2, 0);
                                        else if (mapping & POV_Y)
                                                SendMessage(h, CB_SETCURSEL, (mapping & 3)*2 + 1, 0);
                                        else
                                                SendMessage(h, CB_SETCURSEL, mapping + nr_povs*2, 0);
                                        id += 2;
                                }
                        }
                }
                return TRUE;

                case WM_COMMAND:
                switch (LOWORD(wParam))
                {
                        case IDC_CONFIG_BASE:
                        if (HIWORD(wParam) == CBN_SELCHANGE)
                                rebuild_axis_button_selections(hdlg);
                        break;

                        case IDOK:
                        {
                                id = IDC_CONFIG_BASE + 2;

                                h = GetDlgItem(hdlg, IDC_CONFIG_BASE);
                                joystick_state[joystick_nr].plat_joystick_nr = SendMessage(h, CB_GETCURSEL, 0, 0);

                                if (joystick_state[joystick_nr].plat_joystick_nr)
                                {
                                        for (c = 0; c < joystick_get_axis_count(joystick_config_type); c++)
                                        {
                                                joystick_state[joystick_nr].axis_mapping[c] = get_axis(hdlg, id);
                                                id += 2;
                                        }
                                        for (c = 0; c < joystick_get_button_count(joystick_config_type); c++)
                                        {
                                                h = GetDlgItem(hdlg, id);
                                                joystick_state[joystick_nr].button_mapping[c] = SendMessage(h, CB_GETCURSEL, 0, 0);
                                                id += 2;
                                        }
                                        for (c = 0; c < joystick_get_button_count(joystick_config_type); c++)
                                        {
                                                h = GetDlgItem(hdlg, id);
                                                joystick_state[joystick_nr].pov_mapping[c][0] = get_pov(hdlg, id);
                                                id += 2;
                                                h = GetDlgItem(hdlg, id);
                                                joystick_state[joystick_nr].pov_mapping[c][1] = get_pov(hdlg, id);
                                                id += 2;
                                        }
                                }
                        }
			joystickconfig_changed = 1;
                        EndDialog(hdlg, 0);
                        return TRUE;
                        case IDCANCEL:
			joystickconfig_changed = 0;
                        EndDialog(hdlg, 0);
                        return TRUE;
                }
                break;
        }
        return FALSE;
}

uint8_t joystickconfig_open(HWND hwnd, int joy_nr, int type)
{
        uint16_t *data_block = malloc(16384);
        uint16_t *data;
        DLGTEMPLATE *dlg = (DLGTEMPLATE *)data_block;
        DLGITEMTEMPLATE *item;
        int y = 10;
        int id = IDC_CONFIG_BASE;
        int c;
	char s[269];

	joystickconfig_changed = 0;

        joystick_nr = joy_nr;
        joystick_config_type = type;

        memset(data_block, 0, 4096);

        dlg->style = DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU;
        dlg->x  = 10;
        dlg->y  = 10;
        dlg->cx = 220;
        dlg->cy = 70;

        data = (uint16_t *)(dlg + 1);

        *data++ = 0; /*no menu*/
        *data++ = 0; /*predefined dialog box class*/
        data += MultiByteToWideChar(CP_ACP, 0, "Joystick Configuration", -1, data, 50);

        *data++ = 9; /*Point*/
        data += MultiByteToWideChar(CP_ACP, 0, "Segoe UI", -1, data, 50);

        if (((uintptr_t)data) & 2)
                data++;


        /*Combo box*/
        item = (DLGITEMTEMPLATE *)data;
        item->x = 70;
        item->y = y;
        item->id = id++;

        item->cx = 140;
        item->cy = 150;

        item->style = WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | WS_VSCROLL;

        data = (uint16_t *)(item + 1);
        *data++ = 0xFFFF;
        *data++ = 0x0085;    /* combo box class */

        data += MultiByteToWideChar(CP_ACP, 0, "Device", -1, data, 256);
        *data++ = 0;              /* no creation data */

        if (((uintptr_t)data) & 2)
                data++;

        /*Static text*/
        item = (DLGITEMTEMPLATE *)data;
        item->x = 10;
        item->y = y + 2;
        item->id = id++;

        item->cx = 60;
        item->cy = 15;

        item->style = WS_CHILD | WS_VISIBLE;

        data = (uint16_t *)(item + 1);
        *data++ = 0xFFFF;
        *data++ = 0x0082;    /* static class */

        data += MultiByteToWideChar(CP_ACP, 0, "Device", -1, data, 256);
        *data++ = 0;              /* no creation data */

        if (((uintptr_t)data) & 2)
                data++;

        y += 20;


        for (c = 0; c < joystick_get_axis_count(type); c++)
        {
                /*Combo box*/
                item = (DLGITEMTEMPLATE *)data;
                item->x = 70;
                item->y = y;
                item->id = id++;

                item->cx = 140;
                item->cy = 150;

                item->style = WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | WS_VSCROLL;

                data = (uint16_t *)(item + 1);
                *data++ = 0xFFFF;
                *data++ = 0x0085;    /* combo box class */

                data += MultiByteToWideChar(CP_ACP, 0, joystick_get_axis_name(type, c), -1, data, 256);
                *data++ = 0;              /* no creation data */

                if (((uintptr_t)data) & 2)
                        data++;

                /*Static text*/
                item = (DLGITEMTEMPLATE *)data;
                item->x = 10;
                item->y = y + 2;
                item->id = id++;

                item->cx = 60;
                item->cy = 15;

                item->style = WS_CHILD | WS_VISIBLE;

                data = (uint16_t *)(item + 1);
                *data++ = 0xFFFF;
                *data++ = 0x0082;    /* static class */

                data += MultiByteToWideChar(CP_ACP, 0, joystick_get_axis_name(type, c), -1, data, 256);
                *data++ = 0;              /* no creation data */

                if (((uintptr_t)data) & 2)
                        data++;

                y += 20;
        }

        for (c = 0; c < joystick_get_button_count(type); c++)
        {
                /*Combo box*/
                item = (DLGITEMTEMPLATE *)data;
                item->x = 70;
                item->y = y;
                item->id = id++;

                item->cx = 140;
                item->cy = 150;

                item->style = WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | WS_VSCROLL;

                data = (uint16_t *)(item + 1);
                *data++ = 0xFFFF;
                *data++ = 0x0085;    /* combo box class */

                data += MultiByteToWideChar(CP_ACP, 0, joystick_get_button_name(type, c), -1, data, 256);
                *data++ = 0;              /* no creation data */

                if (((uintptr_t)data) & 2)
                        data++;

                /*Static text*/
                item = (DLGITEMTEMPLATE *)data;
                item->x = 10;
                item->y = y + 2;
                item->id = id++;

                item->cx = 60;
                item->cy = 15;

                item->style = WS_CHILD | WS_VISIBLE;

                data = (uint16_t *)(item + 1);
                *data++ = 0xFFFF;
                *data++ = 0x0082;    /* static class */

                data += MultiByteToWideChar(CP_ACP, 0, joystick_get_button_name(type, c), -1, data, 256);
                *data++ = 0;              /* no creation data */

                if (((uintptr_t)data) & 2)
                        data++;

                y += 20;
        }

        for (c = 0; c < joystick_get_pov_count(type)*2; c++)
        {
                /*Combo box*/
                item = (DLGITEMTEMPLATE *)data;
                item->x = 70;
                item->y = y;
                item->id = id++;

                item->cx = 140;
                item->cy = 150;

                item->style = WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | WS_VSCROLL;

                data = (uint16_t *)(item + 1);
                *data++ = 0xFFFF;
                *data++ = 0x0085;    /* combo box class */

                if (c & 1)
                        sprintf(s, "%s (Y axis)", joystick_get_pov_name(type, c/2));
                else
                        sprintf(s, "%s (X axis)", joystick_get_pov_name(type, c/2));
                data += MultiByteToWideChar(CP_ACP, 0, s, -1, data, 256);
                *data++ = 0;              /* no creation data */

                if (((uintptr_t)data) & 2)
                        data++;

                /*Static text*/
                item = (DLGITEMTEMPLATE *)data;
                item->x = 10;
                item->y = y + 2;
                item->id = id++;

                item->cx = 60;
                item->cy = 15;

                item->style = WS_CHILD | WS_VISIBLE;

                data = (uint16_t *)(item + 1);
                *data++ = 0xFFFF;
                *data++ = 0x0082;    /* static class */

                data += MultiByteToWideChar(CP_ACP, 0, s, -1, data, 256);
                *data++ = 0;              /* no creation data */

                if (((uintptr_t)data) & 2)
                        data++;

                y += 20;
        }

        dlg->cdit = (id - IDC_CONFIG_BASE) + 2;

        item = (DLGITEMTEMPLATE *)data;
        item->x = 100;
        item->y = y + 5;
        item->cx = 50;
        item->cy = 14;
        item->id = IDOK;  /* OK button identifier */
        item->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON;

        data = (uint16_t *)(item + 1);
        *data++ = 0xFFFF;
        *data++ = 0x0080;    /* button class */

        data += MultiByteToWideChar(CP_ACP, 0, "OK", -1, data, 50);
        *data++ = 0;              /* no creation data */

        if (((uintptr_t)data) & 2)
                data++;

        item = (DLGITEMTEMPLATE *)data;
        item->x = 160;
        item->y = y + 5;
        item->cx = 50;
        item->cy = 14;
        item->id = IDCANCEL;  /* Cancel button identifier */
        item->style = WS_CHILD | WS_VISIBLE;

        data = (uint16_t *)(item + 1);
        *data++ = 0xFFFF;
        *data++ = 0x0080;    /* button class */

        data += MultiByteToWideChar(CP_ACP, 0, "Cancel", -1, data, 50);
        *data++ = 0;              /* no creation data */

        dlg->cy = y + 25;

        DialogBoxIndirect(hinstance, dlg, hwnd, joystickconfig_dlgproc);

        free(data_block);

	return joystickconfig_changed;
}
