#include <windows.h>
#include <shellapi.h>
#include <stdio.h>
#include "resource.h"

static const char *INI_RELATIVE = "\\HolosHanafuda_Data\\Plugins\\x86_64\\directip.ini";
static const char *TENOKE_RELATIVE = "\\HolosHanafuda_Data\\Plugins\\x86_64\\tenoke.ini";
static const char *GAME_EXE = "\\HolosHanafuda.exe";
static const char *SECTION = "Network";

static const char *KEYS[] = {
    "Role", "IP", "Port", "MaxMembers", "Platform",
    "WinRate", "IsCrossPlatform", "AppVersion", "OnlineRule"
};
static const char *DEFAULTS[] = {
    "Client", "127.0.0.1", "7777", "4", "Steam",
    "10", "true", "1.2.0", "Koikoi"
};
static const char *COMMENTS[] = {
    "Client or Host", "Target IP address", "UDP port",
    "Maximum lobby members", "Lobby platform identifier",
    "Win rate (for matchmaking)", "Cross-platform flag (true/false)",
    "App version (for matchmaking)", "Online rule (for matchmaking)"
};

static char g_gameDir[MAX_PATH];

static void GetIniPath(char *buf, size_t size) {
    _snprintf(buf, size, "%s%s", g_gameDir, INI_RELATIVE);
}
static void GetTenokePath(char *buf, size_t size) {
    _snprintf(buf, size, "%s%s", g_gameDir, TENOKE_RELATIVE);
}
static void GetExePath(char *buf, size_t size) {
    _snprintf(buf, size, "%s%s", g_gameDir, GAME_EXE);
}

static char *ReadFileContent(const char *path, DWORD *outSize) {
    *outSize = 0;
    HANDLE hFile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL,
                                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) return NULL;
    DWORD size = GetFileSize(hFile, NULL);
    if (size == 0 || size > 65536) { CloseHandle(hFile); return NULL; }
    char *content = (char *)malloc(size + 2);
    DWORD read;
    if (!ReadFile(hFile, content, size, &read, NULL) || read != size) {
        free(content); CloseHandle(hFile); return NULL;
    }
    content[size] = '\0';
    content[size + 1] = '\0';
    CloseHandle(hFile);
    *outSize = size;
    return content;
}

static int WriteFileContent(const char *path, const char *data, DWORD len) {
    HANDLE hFile = CreateFileA(path, GENERIC_WRITE, FILE_SHARE_READ, NULL,
                                CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) return 0;
    DWORD written;
    int ok = WriteFile(hFile, data, len, &written, NULL) && written == len;
    CloseHandle(hFile);
    return ok;
}

static const char *ReadTenokeUser(const char *path) {
    static char value[128];
    value[0] = '\0';

    DWORD size;
    char *content = ReadFileContent(path, &size);
    if (!content) return value;

    // Check for UTF-16LE BOM
    char *p = content;
    if (size >= 2 && (BYTE)p[0] == 0xFF && (BYTE)p[1] == 0xFE) {
        // Convert UTF-16LE to ASCII: skip BOM, take low bytes
        char *ascii = (char *)malloc(size / 2 + 1);
        int j = 0;
        for (DWORD i = 2; i + 1 < size; i += 2) {
            if (p[i] == '\r') continue;
            ascii[j++] = p[i];
        }
        ascii[j] = '\0';
        free(content);
        content = ascii;
        size = j;
    }

    // Find "user =" and extract quoted value
    char *userPos = strstr(content, "user");
    while (userPos) {
        char *after = userPos + 4;
        while (*after == ' ' || *after == '\t') after++;
        if (*after == '=') {
            after++;
            while (*after == ' ' || *after == '\t') after++;
            if (*after == '"') {
                after++;
                int i = 0;
                while (*after && *after != '"' && i < (int)sizeof(value) - 1)
                    value[i++] = *after++;
                value[i] = '\0';
                break;
            }
        }
        userPos = strstr(after, "user");
    }

    free(content);
    return value;
}

static void WriteTenokeUser(const char *path, const char *newUser) {
    DWORD size;
    char *content = ReadFileContent(path, &size);
    if (!content) return;

    int isUtf16 = (size >= 2 && (BYTE)content[0] == 0xFF && (BYTE)content[1] == 0xFE);
    char *work = content;
    DWORD workSize = size;

    if (isUtf16) {
        char *ascii = (char *)malloc(size / 2 + 1);
        int j = 0;
        for (DWORD i = 2; i + 1 < size; i += 2)
            ascii[j++] = content[i];
        ascii[j] = '\0';
        work = ascii;
        workSize = j;
    }

    char *valueStart = NULL;
    char *valueEnd = NULL;
    char *userPos = strstr(work, "user");

    while (userPos) {
        char *after = userPos + 4;
        while (*after == ' ' || *after == '\t') after++;
        if (*after == '=') {
            after++;
            while (*after == ' ' || *after == '\t') after++;
            if (*after == '"') {
                valueStart = after + 1;
                valueEnd = valueStart;
                while (*valueEnd && *valueEnd != '"') valueEnd++;
                break;
            }
        }
        userPos = strstr(after, "user");
    }

    char asciiBuf[32768];
    int pos = 0;

    if (valueStart) {
        int beforeQuote = (int)(valueStart - work);
        memcpy(asciiBuf, work, beforeQuote);
        pos = beforeQuote;
        int newLen = (int)strlen(newUser);
        memcpy(asciiBuf + pos, newUser, newLen);
        pos += newLen;
        int afterQuote = (int)(work + workSize - valueEnd);
        memcpy(asciiBuf + pos, valueEnd, afterQuote);
        pos += afterQuote;
    } else {
        pos = _snprintf(asciiBuf, sizeof(asciiBuf),
                        "%s\r\nuser = \"%s\"\r\n", work, newUser);
    }

    if (isUtf16) {
        char utf16Buf[65536];
        int upos = 0;
        utf16Buf[upos++] = (char)0xFF;
        utf16Buf[upos++] = (char)0xFE;
        for (int i = 0; i < pos; i++) {
            utf16Buf[upos++] = asciiBuf[i];
            utf16Buf[upos++] = '\0';
        }
        WriteFileContent(path, utf16Buf, upos);
        free(work);
    } else {
        WriteFileContent(path, asciiBuf, pos);
    }

    free(content);
}

static void LoadConfig(HWND dlg) {
    char ini[MAX_PATH];
    GetIniPath(ini, sizeof(ini));
    char buf[256];

    GetPrivateProfileStringA(SECTION, KEYS[0], DEFAULTS[0], buf, sizeof(buf), ini);
    if (SendDlgItemMessageA(dlg, IDC_ROLE, CB_SELECTSTRING, -1, (LPARAM)buf) == CB_ERR)
        SendDlgItemMessageA(dlg, IDC_ROLE, CB_SETCURSEL, 0, 0);

    GetPrivateProfileStringA(SECTION, KEYS[1], DEFAULTS[1], buf, sizeof(buf), ini);
    SetDlgItemTextA(dlg, IDC_IP, buf);

    GetPrivateProfileStringA(SECTION, KEYS[2], DEFAULTS[2], buf, sizeof(buf), ini);
    SetDlgItemTextA(dlg, IDC_PORT, buf);

    GetPrivateProfileStringA(SECTION, KEYS[3], DEFAULTS[3], buf, sizeof(buf), ini);
    SetDlgItemTextA(dlg, IDC_MAX_MEMBERS, buf);

    GetPrivateProfileStringA(SECTION, KEYS[4], DEFAULTS[4], buf, sizeof(buf), ini);
    SetDlgItemTextA(dlg, IDC_PLATFORM, buf);

    GetPrivateProfileStringA(SECTION, KEYS[5], DEFAULTS[5], buf, sizeof(buf), ini);
    SetDlgItemTextA(dlg, IDC_WIN_RATE, buf);

    GetPrivateProfileStringA(SECTION, KEYS[6], DEFAULTS[6], buf, sizeof(buf), ini);
    if (SendDlgItemMessageA(dlg, IDC_CROSS_PLATFORM, CB_SELECTSTRING, -1, (LPARAM)buf) == CB_ERR)
        SendDlgItemMessageA(dlg, IDC_CROSS_PLATFORM, CB_SETCURSEL, 0, 0);

    GetPrivateProfileStringA(SECTION, KEYS[7], DEFAULTS[7], buf, sizeof(buf), ini);
    SetDlgItemTextA(dlg, IDC_APP_VERSION, buf);

    GetPrivateProfileStringA(SECTION, KEYS[8], DEFAULTS[8], buf, sizeof(buf), ini);
    SetDlgItemTextA(dlg, IDC_ONLINE_RULE, buf);

    char tenoke[MAX_PATH];
    GetTenokePath(tenoke, sizeof(tenoke));
    if (GetFileAttributesA(tenoke) != INVALID_FILE_ATTRIBUTES) {
        ShowWindow(GetDlgItem(dlg, IDC_TENOKE_USER), SW_SHOW);
        ShowWindow(GetDlgItem(dlg, IDC_TENOKE_LABEL), SW_SHOW);
        const char *user = ReadTenokeUser(tenoke);
        SetDlgItemTextA(dlg, IDC_TENOKE_USER, user);
    } else {
        ShowWindow(GetDlgItem(dlg, IDC_TENOKE_USER), SW_HIDE);
        ShowWindow(GetDlgItem(dlg, IDC_TENOKE_LABEL), SW_HIDE);
    }
}

static void EnsurePathExists(const char *path) {
    char dir[MAX_PATH];
    strncpy(dir, path, MAX_PATH);
    for (char *p = dir; *p; p++) {
        if (*p == '\\' || *p == '/') {
            char sep = *p;
            *p = '\0';
            CreateDirectoryA(dir, NULL);
            *p = sep;
        }
    }
}

static void SaveConfig(HWND dlg) {
    char ini[MAX_PATH];
    GetIniPath(ini, sizeof(ini));
    char buf[256];

    EnsurePathExists(ini);

    for (int i = 0; i < 9; i++) {
        GetDlgItemTextA(dlg, IDC_ROLE + i, buf, sizeof(buf));
        if (buf[0])
            WritePrivateProfileStringA(SECTION, KEYS[i], buf, ini);
    }

    char tenoke[MAX_PATH];
    GetTenokePath(tenoke, sizeof(tenoke));
    HWND hTenoke = GetDlgItem(dlg, IDC_TENOKE_USER);
    if (hTenoke && IsWindowVisible(hTenoke)) {
        GetDlgItemTextA(dlg, IDC_TENOKE_USER, buf, sizeof(buf));
        if (buf[0])
            WriteTenokeUser(tenoke, buf);
    }

    MessageBoxA(dlg, "Configuration saved.", "EOSHoloLauncher",
                MB_OK | MB_ICONINFORMATION);
}

static void ResetConfig(HWND dlg) {
    char ini[MAX_PATH];
    GetIniPath(ini, sizeof(ini));

    EnsurePathExists(ini);

    for (int i = 0; i < 9; i++)
        WritePrivateProfileStringA(SECTION, KEYS[i], NULL, ini);

    HANDLE hFile = CreateFileA(ini, GENERIC_WRITE, FILE_SHARE_READ, NULL,
                                OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile != INVALID_HANDLE_VALUE) {
        DWORD read;
        char old[4096] = {0};
        DWORD oldLen = GetFileSize(hFile, NULL);
        if (oldLen > 0 && oldLen < sizeof(old) - 1)
            ReadFile(hFile, old, oldLen, &read, NULL);

        char tmp[8192];
        int pos = _snprintf(tmp, sizeof(tmp),
            "; To override default values, uncomment and edit the lines below:\n"
            "\n"
            "[Network]\n");

        for (int i = 0; i < 9; i++)
            pos += _snprintf(tmp + pos, sizeof(tmp) - pos,
                "; %s=%s    ; %s\n", KEYS[i], DEFAULTS[i], COMMENTS[i]);

        pos += _snprintf(tmp + pos, sizeof(tmp) - pos, "\n");

        char *line = old;
        bool inNetwork = false;
        while (line && *line && (size_t)(line - old) < oldLen) {
            char *nl = strchr(line, '\n');
            size_t len = nl ? (size_t)(nl - line) : strlen(line);
            char saved = line[len];
            line[len] = '\0';

            char *trim = line;
            while (*trim == ' ' || *trim == '\t' || *trim == '\r') trim++;
            if (trim[0] == '[')
                inNetwork = (_stricmp(trim, "[Network]") == 0);

            if (!inNetwork) {
                memcpy(tmp + pos, line, len);
                pos += (int)len;
                tmp[pos++] = '\r';
                tmp[pos++] = '\n';
            }

            if (nl) {
                line[len] = saved;
                line = nl + 1;
            } else {
                break;
            }
        }

        SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
        SetEndOfFile(hFile);
        DWORD written;
        WriteFile(hFile, tmp, pos, &written, NULL);
        CloseHandle(hFile);
    }

    LoadConfig(dlg);
    MessageBoxA(dlg, "Settings reset to defaults.\nExample lines kept for reference.",
                "EOSHoloLauncher", MB_OK | MB_ICONINFORMATION);
}

static INT_PTR CALLBACK AboutDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam) {
    if (msg == WM_INITDIALOG) {
        HICON hIcon = (HICON)LoadImageA(GetModuleHandleA(NULL),
                        MAKEINTRESOURCEA(IDI_MAIN), IMAGE_ICON, 64, 64, LR_DEFAULTCOLOR);
        if (hIcon) {
            SendDlgItemMessageA(dlg, IDC_ABOUT_ICON, STM_SETICON, (WPARAM)hIcon, 0);
            SetPropA(dlg, "ABOUT_ICON", hIcon);
        }
        return TRUE;
    }
    if (msg == WM_CTLCOLORSTATIC) {
        HWND hCtrl = (HWND)lParam;
        if (hCtrl == GetDlgItem(dlg, IDC_ABOUT_LINK)) {
            HDC dc = (HDC)wParam;
            SetTextColor(dc, RGB(0, 102, 204));
            SetBkMode(dc, TRANSPARENT);
            return (INT_PTR)GetStockObject(HOLLOW_BRUSH);
        }
        return FALSE;
    }
    if (msg == WM_SETCURSOR) {
        if ((HWND)wParam == GetDlgItem(dlg, IDC_ABOUT_LINK)) {
            SetCursor(LoadCursorA(NULL, IDC_HAND));
            return TRUE;
        }
        return FALSE;
    }
    if (msg == WM_COMMAND) {
        if (LOWORD(wParam) == IDC_ABOUT_LINK && HIWORD(wParam) == STN_CLICKED) {
            ShellExecuteA(NULL, "open", "https://codeberg.org/Nixietab/EOSSDK-Holos",
                          NULL, NULL, SW_SHOWNORMAL);
            return TRUE;
        }
        if (LOWORD(wParam) == IDOK) {
            EndDialog(dlg, IDOK);
            return TRUE;
        }
    }
    if (msg == WM_CLOSE) {
        EndDialog(dlg, IDCANCEL);
        return TRUE;
    }
    if (msg == WM_NCDESTROY) {
        HICON hIcon = (HICON)RemovePropA(dlg, "ABOUT_ICON");
        if (hIcon) DestroyIcon(hIcon);
        return TRUE;
    }
    return FALSE;
}

static void LaunchGame(HWND dlg) {
    char exe[MAX_PATH];
    GetExePath(exe, sizeof(exe));

    if (GetFileAttributesA(exe) == INVALID_FILE_ATTRIBUTES) {
        char msg[512];
        _snprintf(msg, sizeof(msg),
            "Game executable not found:\n%s\n\n"
            "Make sure the launcher is in the game's root directory.", exe);
        MessageBoxA(dlg, msg, "Error", MB_OK | MB_ICONERROR);
        return;
    }

    SaveConfig(dlg);

    HINSTANCE r = ShellExecuteA(dlg, "open", exe, NULL, g_gameDir, SW_SHOWNORMAL);
    if ((INT_PTR)r <= 32) {
        char msg[256];
        _snprintf(msg, sizeof(msg), "Failed to launch game. Error code: %d.", (INT_PTR)r);
        MessageBoxA(dlg, msg, "Error", MB_OK | MB_ICONERROR);
    } else {
        EndDialog(dlg, IDOK);
    }
}

static INT_PTR CALLBACK DlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
    case WM_INITDIALOG:
        {
            HICON hIcon = LoadIconA(GetModuleHandleA(NULL), MAKEINTRESOURCEA(IDI_MAIN));
            SendMessageA(dlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
            SendMessageA(dlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
        }
        SendDlgItemMessageA(dlg, IDC_ROLE, CB_ADDSTRING, 0, (LPARAM)"Client");
        SendDlgItemMessageA(dlg, IDC_ROLE, CB_ADDSTRING, 0, (LPARAM)"Host");
        SendDlgItemMessageA(dlg, IDC_CROSS_PLATFORM, CB_ADDSTRING, 0, (LPARAM)"true");
        SendDlgItemMessageA(dlg, IDC_CROSS_PLATFORM, CB_ADDSTRING, 0, (LPARAM)"false");
        LoadConfig(dlg);
        {
            int sel = (int)SendDlgItemMessageA(dlg, IDC_ROLE, CB_GETCURSEL, 0, 0);
            EnableWindow(GetDlgItem(dlg, IDC_IP), sel == 0);
        }
        return TRUE;

    case WM_COMMAND:
        if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_ROLE) {
            int sel = (int)SendDlgItemMessageA(dlg, IDC_ROLE, CB_GETCURSEL, 0, 0);
            EnableWindow(GetDlgItem(dlg, IDC_IP), sel == 0);
            return TRUE;
        }
        switch (LOWORD(wParam)) {
        case IDC_SAVE:
            SaveConfig(dlg);
            return TRUE;
        case IDC_RESET:
        case IDM_RESET:
            ResetConfig(dlg);
            return TRUE;
        case IDC_LAUNCH:
            LaunchGame(dlg);
            return TRUE;
        case IDM_ABOUT:
            DialogBoxParamA(GetModuleHandleA(NULL), MAKEINTRESOURCEA(IDD_ABOUT),
                            dlg, AboutDlgProc, 0);
            return TRUE;
        case IDM_EXIT:
            EndDialog(dlg, IDCANCEL);
            return TRUE;
        case IDCANCEL:
            EndDialog(dlg, IDCANCEL);
            return TRUE;
        }
        return FALSE;

    case WM_CLOSE:
        EndDialog(dlg, IDCANCEL);
        return TRUE;
    }
    return FALSE;
}

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int) {
    GetModuleFileNameA(NULL, g_gameDir, MAX_PATH);
    char *s = strrchr(g_gameDir, '\\');
    if (!s) s = strrchr(g_gameDir, '/');
    if (s) *s = '\0';
    else g_gameDir[0] = '\0';

    DialogBoxParamA(hInst, MAKEINTRESOURCEA(IDD_MAIN), NULL, DlgProc, 0);
    return 0;
}
