#include "src/Application.h"

#include <Shlwapi.h>
#include <Windows.h>
#pragma comment(lib, "Shlwapi.lib")
#include <Shlobj.h>
#pragma comment(lib, "Shell32.lib")

#include <string>
#include <vector>

#include "Launcher/Launcher.Windows/resource.h"

int __stdcall BrowseFolderCallback(HWND hwnd, UINT msg, LPARAM lparam, LPARAM data) {
    if (msg == BFFM_INITIALIZED) {
        SendMessageA(hwnd, BFFM_SETSELECTION, true, data);
    }
    return 0;
}

INT_PTR __stdcall DialogProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
    Application *app = (Application *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    switch (msg) {
        case WM_INITDIALOG: {
            app = (Application *)lparam;
            SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)app);

            CheckRadioButton(
                hwnd,
                IDC_RADIO_RENDERER_DIRECTDRAW,
                IDC_RADIO_RENDERER_OPENGL,
                IDC_RADIO_RENDERER_DIRECTDRAW);
            break;
        }

        case WM_COMMAND: {
            if (LOWORD(wparam) == IDC_BUTTON_BROWSE_INSTALL) {
                char choice[2000];
                choice[0] = 0;
                strcpy(choice, app->GetMm7InstallPath().c_str());

                BROWSEINFO bi;
                ZeroMemory(&bi, sizeof(bi));
                bi.lParam = (LPARAM)choice;
                bi.lpfn = BrowseFolderCallback;
                bi.ulFlags = BIF_USENEWUI;
                PIDLIST_ABSOLUTE list = SHBrowseForFolder(&bi);

                SHGetPathFromIDListA(list, choice);
                app->SetMm7InstallPath(std::string(choice));
                SendDlgItemMessageA(hwnd, IDC_EDIT_MM7_INSTALL_DIR, WM_SETTEXT,
                                    0, (LPARAM)choice);

                return TRUE;
            } else if (LOWORD(wparam) == IDC_BUTTON_LAUNCH) {
                char mm7_install_dir[2000];
                GetWindowTextA(GetDlgItem(hwnd, IDC_EDIT_MM7_INSTALL_DIR),
                               mm7_install_dir, 2000);

                app->SetMm7InstallPath(std::string(mm7_install_dir));

                if (IsDlgButtonChecked(hwnd, IDC_RADIO_RENDERER_DIRECTDRAW) == BST_CHECKED) {
                    app->SetRenderer(std::string("DirectDraw"));
                } else if (IsDlgButtonChecked(hwnd, IDC_RADIO_RENDERER_OPENGL) == BST_CHECKED) {
                    app->SetRenderer(std::string("OpenGL"));
                }

                std::string config_errors;
                if (!app->ValidateConfig(config_errors)) {
                    MessageBoxA(hwnd, config_errors.c_str(),
                                "Configuration error", MB_OK);
                } else {
                    PostMessage(hwnd, WM_QUIT, 0, 0);
                }

                return TRUE;
            }
            break;
        }
    }
    return FALSE;
}

Application *Application::Configure(ApplicationConfig &config) {
    this->config = config;
    return this;
}

void Application::SetMm7InstallPath(const std::string &path) {
    this->config.mm7_install_path = path;
}
const std::string &Application::GetMm7InstallPath() const {
    return this->config.mm7_install_path;
}

static std::string GetExeFilename() {
    char buf[2000];
    GetModuleFileNameA(GetModuleHandle(0), buf, 2000);
    return std::string(buf);
}

static std::string GetExePath() {
    auto filename = GetExeFilename();
    return filename.substr(0, filename.find_last_of("\\/"));
}

bool Application::ValidateConfig(std::string &out_errors) {
    out_errors = "";

    std::string mm7_exe_path = config.mm7_install_path + "/MM7.exe";
    if (!PathFileExistsA(mm7_exe_path.c_str())) {
        out_errors =
            "Might and Magic VII exe not found in: " + config.mm7_install_path;
        return false;
    }

    return true;
}

void Application::Run() {
    int init = CoInitializeEx(0, COINIT_APARTMENTTHREADED);  // SHBrowseForFolder

    auto module = GetModuleHandleA(nullptr);
    HWND dialog = CreateDialogParamA(module, MAKEINTRESOURCEA(IDD_FORMVIEW),
                                     nullptr, DialogProc, (LPARAM)this);
    HICON icon =
        (HICON)LoadImageA(module, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 0, 0,
                         LR_DEFAULTCOLOR | LR_DEFAULTSIZE);
    SendMessageA(dialog, WM_SETICON, ICON_BIG, (LPARAM)icon);
    SendDlgItemMessageA(dialog, IDC_EDIT_MM7_INSTALL_DIR, WM_SETTEXT, 0,
                        (LPARAM)config.mm7_install_path.c_str());

    MSG msg;
    while (GetMessageA(&msg, dialog, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessageA(&msg);
    }

    STARTUPINFOA si;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);

    PROCESS_INFORMATION pi;
    ZeroMemory(&pi, sizeof(pi));

    std::string womm_filename = GetExePath() + "/" + "World of Might and Magic.exe";
    std::string command_line = womm_filename + " -window -nointro -nologo -novideo -nomarg -render=" + GetRenderer();

    std::vector<char> cmd(command_line.begin(), command_line.end());
    cmd.push_back(0);
    CreateProcessA(
        womm_filename.c_str(), cmd.data(), nullptr, nullptr, FALSE,
        NORMAL_PRIORITY_CLASS, nullptr,
        config.mm7_install_path.c_str(), &si, &pi);

    // Wait until child process exits.
    WaitForSingleObject(pi.hProcess, INFINITE);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

}
