/*
    Copyright 2016-2017 StapleButter

    This file is part of melonDS.

    melonDS 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.

    melonDS 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 melonDS. If not, see http://www.gnu.org/licenses/.
*/

#include "../types.h"
#include "InputConfig.h"
#include "../Config.h"
#include "scancode_wx2sdl.h"


wxBEGIN_EVENT_TABLE(InputConfigDialog, wxDialog)
    EVT_COMMAND(1001, wxEVT_BUTTON, InputConfigDialog::OnOk)
    EVT_COMMAND(1002, wxEVT_BUTTON, InputConfigDialog::OnCancel)

    EVT_TIMER(wxID_ANY, InputConfigDialog::OnPoll)
    EVT_CHAR_HOOK(InputConfigDialog::OnKeyDown)
wxEND_EVENT_TABLE()


InputConfigDialog::InputConfigDialog(wxWindow* parent)
    : wxDialog(parent, -1, "Input configuration - melonDS")
{
    int keyorder[12] = {0, 1, 10, 11, 5, 4, 6, 7, 9, 8, 3, 2};
    char keylabels[12][8] = {"A:", "B:", "Select:", "Start:", "Right:", "Left:", "Up:", "Down:", "R:", "L:", "X:", "Y:"};

    memcpy(keymapping, Config::KeyMapping, 12*sizeof(int));
    memcpy(joymapping, Config::JoyMapping, 12*sizeof(int));

    wxBoxSizer* vboxmain = new wxBoxSizer(wxVERTICAL);

    {
        wxPanel* p = new wxPanel(this);
        wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);

        wxStaticBox* kbdside = new wxStaticBox(p, wxID_ANY, "Keyboard");
        {
            wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
            wxPanel* p = new wxPanel(kbdside);
            wxFlexGridSizer* grid = new wxFlexGridSizer(2, 3, 8);

            for (int i = 0; i < 12; i++)
            {
                int j = keyorder[i];

                wxStaticText* label = new wxStaticText(p, wxID_ANY, keylabels[j]);
                grid->Add(label, 0, wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL);

                // SDL_GetScancodeName() doesn't take the keyboard layout into account,
                // which can be inconvenient
                const char* keyname = SDL_GetKeyName(SDL_GetKeyFromScancode((SDL_Scancode)keymapping[j]));

                wxStaticText* btn = new wxStaticText(p, 100+j, keyname,
                    wxDefaultPosition, wxDefaultSize,
                    wxALIGN_CENTRE_HORIZONTAL | wxBORDER_SUNKEN | wxST_NO_AUTORESIZE);
                btn->SetMinSize(wxSize(120, btn->GetSize().GetHeight()));
                grid->Add(btn);

                btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
                btn->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));

                btn->Connect(100+j, wxEVT_LEFT_UP, wxMouseEventHandler(InputConfigDialog::OnConfigureKey));
                btn->Connect(100+j, wxEVT_ENTER_WINDOW, wxMouseEventHandler(InputConfigDialog::OnFancybuttonHover));
                btn->Connect(100+j, wxEVT_LEAVE_WINDOW, wxMouseEventHandler(InputConfigDialog::OnFancybuttonHover));
            }

            p->SetSizer(grid);
            sizer->Add(p, 0, wxALL, 15);
            kbdside->SetSizer(sizer);
        }
        sizer->Add(kbdside);

        wxStaticBox* joyside = new wxStaticBox(p, wxID_ANY, "Joystick");
        {
            wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
            wxPanel* p = new wxPanel(joyside);
            wxFlexGridSizer* grid = new wxFlexGridSizer(2, 3, 8);

            for (int i = 0; i < 12; i++)
            {
                int j = keyorder[i];

                wxStaticText* label = new wxStaticText(p, wxID_ANY, keylabels[j]);
                grid->Add(label, 0, wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL);

                char keyname[16];
                JoyMappingName(joymapping[j], keyname);

                wxStaticText* btn = new wxStaticText(p, 200+j, keyname,
                    wxDefaultPosition, wxDefaultSize,
                    wxALIGN_CENTRE_HORIZONTAL | wxBORDER_SUNKEN | wxST_NO_AUTORESIZE);
                btn->SetMinSize(wxSize(120, btn->GetSize().GetHeight()));
                grid->Add(btn);

                btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
                btn->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));

                btn->Connect(200+j, wxEVT_LEFT_UP, wxMouseEventHandler(InputConfigDialog::OnConfigureJoy));
                btn->Connect(200+j, wxEVT_ENTER_WINDOW, wxMouseEventHandler(InputConfigDialog::OnFancybuttonHover));
                btn->Connect(200+j, wxEVT_LEAVE_WINDOW, wxMouseEventHandler(InputConfigDialog::OnFancybuttonHover));
            }

            p->SetSizer(grid);
            sizer->Add(p, 0, wxALL, 15);

            /*wxComboBox* joycombo = new wxComboBox(joyside, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_DROPDOWN | wxCB_READONLY);

            int numjoys = SDL_NumJoysticks();
            if (numjoys > 0)
            {
                for (int i = 0; i < numjoys; i++)
                {
                    const char* name = SDL_JoystickNameForIndex(i);
                    joycombo->Insert(name, i);
                }
            }
            else
            {
                joycombo->Append("(no joystick)");
                joycombo->Enable(false);
            }

            sizer->Add(joycombo, 0, wxALL&(~wxTOP), 15);*/

            joyside->SetSizer(sizer);
        }
        sizer->Add(15, 0);
        sizer->Add(joyside);

        p->SetSizer(sizer);
        vboxmain->Add(p, 0, wxALL&(~wxBOTTOM), 15);
    }

    {
        wxPanel* p = new wxPanel(this);
        wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);

        wxButton* ok = new wxButton(p, 1001, "OK");
        sizer->Add(ok);

        wxButton* cancel = new wxButton(p, 1002, "Cancel");
        sizer->Add(3, 0);
        sizer->Add(cancel);

        p->SetSizer(sizer);
        vboxmain->Add(p, 0, wxALL|wxALIGN_RIGHT, 15);
    }

    SetSizer(vboxmain);
    Fit();

    polltimer = new wxTimer(this);
    pollid = 0;

    njoys = SDL_NumJoysticks();
    if (njoys) joy = SDL_JoystickOpen(0);
}

InputConfigDialog::~InputConfigDialog()
{
    delete polltimer;

    if (njoys) SDL_JoystickClose(0);
}

void InputConfigDialog::OnOk(wxCommandEvent& event)
{
    memcpy(Config::KeyMapping, keymapping, 12*sizeof(int));
    memcpy(Config::JoyMapping, joymapping, 12*sizeof(int));
    Config::Save();

    Close();
}

void InputConfigDialog::OnCancel(wxCommandEvent& event)
{
    Close();
}

void InputConfigDialog::OnKeyDown(wxKeyEvent& event)
{
    if (pollid < 100) return;
    int id = pollid - 100;

    SDL_Scancode code = scancode_wx2sdl(event);

    if (code == SDL_SCANCODE_ESCAPE)
    {
        if (pollid >= 200) polltimer->Stop();
        pollbtn->SetLabel(pollbtntext);
        pollid = 0;

        pollbtn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
        pollbtn->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
        pollbtn->Refresh();
        return;
    }
    else if ((code == SDL_SCANCODE_BACKSPACE) && (pollid >= 200))
    {
        id = pollid - 200;
        if (id >= 12) return;

        joymapping[id] = -1;

        char keyname[16];
        JoyMappingName(joymapping[id], keyname);
        pollbtn->SetLabel(keyname);

        polltimer->Stop();
        pollid = 0;

        pollbtn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
        pollbtn->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
        pollbtn->Refresh();
        return;
    }

    if (id >= 12) return;

    keymapping[id] = code;

    pollbtn->Enable(true);

    const char* keyname = SDL_GetKeyName(SDL_GetKeyFromScancode(code));
    pollbtn->SetLabel(keyname);

    polltimer->Stop();
    pollid = 0;

    pollbtn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
    pollbtn->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
    pollbtn->Refresh();
}

// black magic going on there
// these two event handlers are in the InputConfigDialog class for convenience,
// but when they're called, 'this' points to a wxStaticText instance

void InputConfigDialog::OnConfigureKey(wxMouseEvent& event)
{
    wxStaticText* btn = (wxStaticText*)this;
    InputConfigDialog* parent = (InputConfigDialog*)btn->GetParent()->GetParent()->GetParent()->GetParent();
    if (parent->pollid != 0) return;

    parent->pollbtn = btn;
    parent->pollbtntext = btn->GetLabel();
    parent->pollid = event.GetId();

    btn->SetLabel("[press key]");
    btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT));
    btn->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
    btn->Refresh();
}

void InputConfigDialog::OnConfigureJoy(wxMouseEvent& event)
{
    wxStaticText* btn = (wxStaticText*)this;
    InputConfigDialog* parent = (InputConfigDialog*)btn->GetParent()->GetParent()->GetParent()->GetParent();
    if (parent->pollid != 0) return;

    parent->pollbtn = btn;
    parent->pollbtntext = btn->GetLabel();
    parent->pollid = event.GetId();
    parent->polltimer->Start(50);

    btn->SetLabel("[press button]");
    btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT));
    btn->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
    btn->Refresh();
}

void InputConfigDialog::OnPoll(wxTimerEvent& event)
{
    if (pollid < 200) return;

    int id = pollid - 200;
    if (id >= 12) return;

    int nbuttons = SDL_JoystickNumButtons(joy);
    for (int i = 0; i < nbuttons; i++)
    {
        if (SDL_JoystickGetButton(joy, i))
        {
            joymapping[id] = i;

            char keyname[16];
            JoyMappingName(joymapping[id], keyname);
            pollbtn->SetLabel(keyname);

            polltimer->Stop();
            pollid = 0;

            pollbtn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
            pollbtn->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
            pollbtn->Refresh();
            return;
        }
    }

    u8 blackhat = SDL_JoystickGetHat(joy, 0);
    if (blackhat)
    {
        if      (blackhat & 0x1) blackhat = 0x1;
        else if (blackhat & 0x2) blackhat = 0x2;
        else if (blackhat & 0x4) blackhat = 0x4;
        else                     blackhat = 0x8;

        joymapping[id] = 0x100 | blackhat;

        char keyname[16];
        JoyMappingName(joymapping[id], keyname);
        pollbtn->SetLabel(keyname);

        polltimer->Stop();
        pollid = 0;

        pollbtn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
        pollbtn->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
        pollbtn->Refresh();
        return;
    }
}

void InputConfigDialog::OnFancybuttonHover(wxMouseEvent& event)
{
    wxStaticText* btn = (wxStaticText*)this;
    InputConfigDialog* parent = (InputConfigDialog*)btn->GetParent()->GetParent()->GetParent()->GetParent();
    if (event.GetId() == parent->pollid) return;

    if (event.Entering())
    {
        btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT));
        btn->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
    }
    else
    {
        btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
        btn->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
    }
    btn->Refresh();
}

void InputConfigDialog::JoyMappingName(int id, char* str)
{
    if (id < 0)
    {
        strcpy(str, "None");
        return;
    }

    if (id & 0x100)
    {
        switch (id & 0xF)
        {
        case 0x1: strcpy(str, "Up"); break;
        case 0x2: strcpy(str, "Right"); break;
        case 0x4: strcpy(str, "Down"); break;
        case 0x8: strcpy(str, "Left"); break;
        }
    }
    else
    {
        sprintf(str, "Button %d", id+1);
    }
}