#include "wx-app.h"

#include <wx/xrc/xmlres.h>
#include <wx/event.h>
#include "wx-utils.h"
#include "wx-common.h"

#ifdef _WIN32
#define BITMAP WINDOWS_BITMAP
#include <windows.h>
#include <windowsx.h>
#undef BITMAP
#endif

extern "C"
{
        int wx_load_config(void*);
        int wx_start(void*);
        int wx_stop(void*);
        void wx_show(void*);
        void wx_handle_command(void*, int, int);

        int start_emulation(void*);
        int resume_emulation();
        int pause_emulation();
        int stop_emulation();
}

extern int config_override;

extern void InitXmlResource();

wxDEFINE_EVENT(WX_CALLBACK_EVENT, CallbackEvent);
wxDEFINE_EVENT(WX_EXIT_EVENT, wxCommandEvent);
wxDEFINE_EVENT(WX_STOP_EMULATION_EVENT, wxCommandEvent);
wxDEFINE_EVENT(WX_STOP_EMULATION_NOW_EVENT, wxCommandEvent);
wxDEFINE_EVENT(WX_EXIT_COMPLETE_EVENT, wxCommandEvent);
wxDEFINE_EVENT(WX_SHOW_WINDOW_EVENT, wxCommandEvent);
wxDEFINE_EVENT(WX_POPUP_MENU_EVENT, PopupMenuEvent);
#ifdef _WIN32
wxDEFINE_EVENT(WX_WIN_SEND_MESSAGE_EVENT, WinSendMessageEvent);
#endif

wxBEGIN_EVENT_TABLE(Frame, wxFrame)
wxEND_EVENT_TABLE()

wxIMPLEMENT_APP_NO_MAIN(App);

App::App()
{
        this->frame = NULL;
}

bool App::OnInit()
{
        wxImage::AddHandler( new wxPNGHandler );
        wxXmlResource::Get()->InitAllHandlers();
//        if (!wxXmlResource::Get()->Load("src/pc.xrc"))
//        {
//                std::cout << "Could not load resource file" << std::endl;
//                return false;
//        }
        InitXmlResource();

        frame = new Frame(this, "null frame", wxPoint(500, 500),
                        wxSize(100, 100));
        frame->Start();
        return true;
}

int App::OnRun()
{
        return wxApp::OnRun();
}

#include <sstream>

Frame::Frame(App* app, const wxString& title, const wxPoint& pos,
                const wxSize& size) :
                wxFrame(NULL, wxID_ANY, title, pos, size, 0)//wxDEFAULT_FRAME_STYLE & ~(wxRESIZE_BORDER))
{
        this->closing = false;
        this->menu = wxXmlResource::Get()->LoadMenu(wxT("main_menu"));

        Bind(wxEVT_CLOSE_WINDOW, &Frame::OnClose, this);
        Bind(wxEVT_MENU, &Frame::OnCommand, this);
        Bind(wxEVT_TOOL, &Frame::OnCommand, this);
        Bind(WX_SHOW_WINDOW_EVENT, &Frame::OnShowWindowEvent, this);
        Bind(WX_POPUP_MENU_EVENT, &Frame::OnPopupMenuEvent, this);
        Bind(WX_EXIT_EVENT, &Frame::OnExitEvent, this);
        Bind(WX_EXIT_COMPLETE_EVENT, &Frame::OnExitCompleteEvent, this);
        Bind(WX_STOP_EMULATION_EVENT, &Frame::OnStopEmulationEvent, this);
        Bind(WX_STOP_EMULATION_NOW_EVENT, &Frame::OnStopEmulationNowEvent, this);
        Bind(WX_CALLBACK_EVENT, &Frame::OnCallbackEvent, this);
#ifdef _WIN32
        Bind(WX_WIN_SEND_MESSAGE_EVENT, &Frame::OnWinSendMessageEvent, this);
#endif

        CenterOnScreen();
}

void Frame::Start()
{
        if (wx_start(this))
                ShowConfigSelection();
        else
                Quit(0);
}

void Frame::ShowConfigSelection()
{
        if (wx_load_config(this))
                start_emulation(this);
        else
                Quit(1);
}

void Frame::OnCallbackEvent(CallbackEvent& event)
{
        WX_CALLBACK callback = event.GetCallback();
        callback(event.GetData());
}

void Frame::OnStopEmulationEvent(wxCommandEvent& event)
{
        if (emulation_state != EMULATION_STOPPED)
        {
                pause_emulation();
                int ret = wxID_OK;

                if (confirm_on_stop_emulation)
                {
                        wxDialog dlg;
                        wxXmlResource::Get()->LoadDialog(&dlg, this, "ConfirmRememberDlg");
                        dlg.FindWindow("IDC_CONFIRM_LABEL")->SetLabel("Stop emulation?");
                        dlg.FindWindow("IDC_CONFIRM_REMEMBER")->SetLabel("Do not ask again");
                        dlg.Fit();
                        ret = dlg.ShowModal();
                        if (ret == wxID_OK)
                                confirm_on_stop_emulation = !((wxCheckBox*)dlg.FindWindow("IDC_CONFIRM_REMEMBER"))->IsChecked();
                }

                if (ret == wxID_OK)
                {
                        stop_emulation();
                        if (!config_override)
                                ShowConfigSelection();
                        else
                                Quit(1);
                }
                else
                        resume_emulation();
        }
}

void Frame::OnStopEmulationNowEvent(wxCommandEvent& event)
{
        stop_emulation();
        if (!config_override)
                ShowConfigSelection();
        else
                Quit(1);
}

void Frame::OnShowWindowEvent(wxCommandEvent& event)
{
        wxWindow* window = (wxWindow*)event.GetEventObject();
        int shown = window->IsShown();
        int value = event.GetInt();
        if (value < 0)
                window->Show(!shown);
        else
                window->Show(value > 0);
        if (!shown)
                window->Refresh();
}

void Frame::OnPopupMenuEvent(PopupMenuEvent& event)
{
        wxWindow* window = event.GetWindow();
        wxMenu* menu = event.GetMenu();
        int* x = event.GetX();
        int* y = event.GetY();

        if (x && y)
                window->PopupMenu(menu, *x, *y);
        else
                window->PopupMenu(menu);
}

void Frame::OnCommand(wxCommandEvent& event)
{
        wx_handle_command(this, event.GetId(), event.IsChecked());
}

void Frame::OnClose(wxCloseEvent& event)
{
        wx_exit(this, 0);
}

wxMenu* Frame::GetMenu()
{
        return menu;
}

/* Exit */

void Frame::Quit(bool stop_emulator)
{
        if (closing)
                return;
        closing = true;
        if (stop_emulator)
        {
                if (!wx_stop(this))
                {
                        // cancel quit
                        closing = false;
                        return;
                }
        }
        Destroy();
}

void Frame::OnExitEvent(wxCommandEvent& event)
{
        if (closing)
                return;
        closing = true;
        CExitThread* exitThread = new CExitThread(this);
        exitThread->Run();
}

void Frame::OnExitCompleteEvent(wxCommandEvent& event)
{
        if (event.GetInt())
                Destroy();
        else
                closing = false;
}

CExitThread::CExitThread(Frame* frame)
{
	this->frame = frame;
}

wxThread::ExitCode CExitThread::Entry()
{
        wxCommandEvent* event = new wxCommandEvent(WX_EXIT_COMPLETE_EVENT, wxID_ANY);
        event->SetInt(wx_stop(frame));
        wxQueueEvent(frame, event);
	return 0;
}

#ifdef _WIN32
void Frame::OnWinSendMessageEvent(WinSendMessageEvent& event)
{
        SendMessage((HWND)event.GetHWND(), event.GetMessage(), event.GetWParam(), event.GetLParam());
}
#endif
