#define _CRT_SECURE_NO_WARNINGS
// installer.c — Self-contained Windows installer for codeMAX
//
// Uses Win32 PropertySheet wizard for the UI, embeds payload EXEs as
// resources, and handles PATH, Start Menu, and Add/Remove Programs.
//
// When invoked with /uninstall, runs the uninstaller instead.

#include <windows.h>
#include <commctrl.h>
#include <shlobj.h>
#include <objbase.h>
#include <shobjidl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "shell32.lib")
#pragma comment(lib, "advapi32.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "uuid.lib")
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

#include "installer.h"

// Use codebase types
typedef unsigned char      U8;
typedef unsigned short     U16;
typedef unsigned int       U32;
typedef unsigned long long U64;
typedef int                S32;
typedef int                B32;

#define ArrayCount(a) (sizeof(a) / sizeof((a)[0]))

////////////////////////////////
// Globals

static wchar_t g_install_dir[MAX_PATH];
static B32 g_add_to_path = 1;
static HINSTANCE g_hinst;

////////////////////////////////
// Helpers

static B32 extract_resource(S32 resource_id, const wchar_t *dest_path) {
    HRSRC res = FindResourceW(NULL, MAKEINTRESOURCEW(resource_id), (LPCWSTR)RT_RCDATA);
    if (!res) return 0;
    HGLOBAL h = LoadResource(NULL, res);
    if (!h) return 0;
    void *data = LockResource(h);
    DWORD size = SizeofResource(NULL, res);
    if (!data || size == 0) return 0;

    HANDLE f = CreateFileW(dest_path, GENERIC_WRITE, 0, NULL,
                           CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (f == INVALID_HANDLE_VALUE) return 0;
    DWORD written;
    WriteFile(f, data, size, &written, NULL);
    CloseHandle(f);
    return written == size;
}

static B32 create_directory_recursive(const wchar_t *path) {
    wchar_t tmp[MAX_PATH];
    wcscpy_s(tmp, MAX_PATH, path);
    for (wchar_t *p = tmp + 3; *p; p++) { // skip "C:\"
        if (*p == L'\\' || *p == L'/') {
            *p = 0;
            CreateDirectoryW(tmp, NULL);
            *p = L'\\';
        }
    }
    return CreateDirectoryW(tmp, NULL) || GetLastError() == ERROR_ALREADY_EXISTS;
}

static B32 create_shortcut(const wchar_t *lnk_path, const wchar_t *target,
                           const wchar_t *work_dir, const wchar_t *description,
                           const wchar_t *icon_path) {
    HRESULT hr;
    IShellLinkW *sl = NULL;
    hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
                          &IID_IShellLinkW, (void **)&sl);
    if (FAILED(hr)) return 0;

    sl->lpVtbl->SetPath(sl, target);
    sl->lpVtbl->SetWorkingDirectory(sl, work_dir);
    if (description) sl->lpVtbl->SetDescription(sl, description);
    if (icon_path) sl->lpVtbl->SetIconLocation(sl, icon_path, 0);

    IPersistFile *pf = NULL;
    hr = sl->lpVtbl->QueryInterface(sl, &IID_IPersistFile, (void **)&pf);
    B32 ok = 0;
    if (SUCCEEDED(hr)) {
        ok = SUCCEEDED(pf->lpVtbl->Save(pf, lnk_path, TRUE));
        pf->lpVtbl->Release(pf);
    }
    sl->lpVtbl->Release(sl);
    return ok;
}

static B32 path_add(const wchar_t *dir) {
    HKEY key;
    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                      L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment",
                      0, KEY_READ | KEY_WRITE, &key) != ERROR_SUCCESS) return 0;

    wchar_t buf[8192] = {0};
    DWORD buf_size = sizeof(buf) - sizeof(wchar_t);
    DWORD type = REG_EXPAND_SZ;
    RegQueryValueExW(key, L"Path", NULL, &type, (BYTE *)buf, &buf_size);

    // Check if already present
    if (wcsstr(buf, dir)) {
        RegCloseKey(key);
        return 1;
    }

    // Append
    S32 len = (S32)wcslen(buf);
    if (len > 0 && buf[len - 1] != L';')
        wcscat_s(buf, ArrayCount(buf), L";");
    wcscat_s(buf, ArrayCount(buf), dir);

    RegSetValueExW(key, L"Path", 0, type,
                   (BYTE *)buf, (DWORD)((wcslen(buf) + 1) * sizeof(wchar_t)));
    RegCloseKey(key);
    return 1;
}

static void path_remove(const wchar_t *dir) {
    HKEY key;
    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                      L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment",
                      0, KEY_READ | KEY_WRITE, &key) != ERROR_SUCCESS) return;

    wchar_t buf[8192] = {0};
    DWORD buf_size = sizeof(buf) - sizeof(wchar_t);
    DWORD type = REG_EXPAND_SZ;
    RegQueryValueExW(key, L"Path", NULL, &type, (BYTE *)buf, &buf_size);

    wchar_t result[8192] = {0};
    wchar_t *ctx = NULL;
    wchar_t copy[8192];
    wcscpy_s(copy, ArrayCount(copy), buf);

    wchar_t *tok = wcstok_s(copy, L";", &ctx);
    B32 first = 1;
    while (tok) {
        if (_wcsicmp(tok, dir) != 0) {
            if (!first) wcscat_s(result, ArrayCount(result), L";");
            wcscat_s(result, ArrayCount(result), tok);
            first = 0;
        }
        tok = wcstok_s(NULL, L";", &ctx);
    }

    RegSetValueExW(key, L"Path", 0, type,
                   (BYTE *)result, (DWORD)((wcslen(result) + 1) * sizeof(wchar_t)));
    RegCloseKey(key);
}

static void broadcast_env_change(void) {
    SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
                        (LPARAM)L"Environment", SMTO_ABORTIFHUNG, 5000, NULL);
}

////////////////////////////////
// In-memory dialog template builder

typedef struct {
    U8  data[4096];
    S32 len;
} DlgBuf;

static void dlg_align(DlgBuf *b, S32 align) {
    while (b->len % align) b->data[b->len++] = 0;
}

static void dlg_write(DlgBuf *b, const void *src, S32 n) {
    memcpy(b->data + b->len, src, n);
    b->len += n;
}

static void dlg_write16(DlgBuf *b, U16 v) { dlg_write(b, &v, 2); }
static void dlg_write32(DlgBuf *b, U32 v) { dlg_write(b, &v, 4); }

static void dlg_write_wstr(DlgBuf *b, const wchar_t *s) {
    S32 n = (S32)((wcslen(s) + 1) * sizeof(wchar_t));
    dlg_write(b, s, n);
}

static DLGTEMPLATE *build_page_template(DlgBuf *b, S32 w, S32 h) {
    b->len = 0;
    // DLGTEMPLATE
    DLGTEMPLATE *dt = (DLGTEMPLATE *)b->data;
    dlg_write32(b, WS_CHILD | WS_VISIBLE | DS_SHELLFONT); // style
    dlg_write32(b, 0);         // dwExtendedStyle
    dlg_write16(b, 0);         // cdit (control count, filled later)
    dlg_write16(b, 0);         // x
    dlg_write16(b, 0);         // y
    dlg_write16(b, (U16)w);   // cx
    dlg_write16(b, (U16)h);   // cy
    dlg_write16(b, 0);         // menu (none)
    dlg_write16(b, 0);         // class (default)
    dlg_write_wstr(b, L"");    // title
    // DS_SHELLFONT font
    dlg_write16(b, 9);         // point size
    dlg_write_wstr(b, L"Segoe UI");
    return dt;
}

static void add_control(DlgBuf *b, DLGTEMPLATE *dt, U32 style, S32 x, S32 y,
                         S32 w, S32 h, S32 id, const wchar_t *cls, const wchar_t *text) {
    dlg_align(b, 4);
    // DLGITEMTEMPLATE
    dlg_write32(b, style | WS_CHILD | WS_VISIBLE); // style
    dlg_write32(b, 0);         // dwExtendedStyle
    dlg_write16(b, (U16)x);
    dlg_write16(b, (U16)y);
    dlg_write16(b, (U16)w);
    dlg_write16(b, (U16)h);
    dlg_write16(b, (U16)id);
    dlg_write_wstr(b, cls);
    dlg_write_wstr(b, text);
    dlg_write16(b, 0);         // creation data
    dt->cdit++;
}

////////////////////////////////
// Page IDs for controls

#define IDC_DIR_EDIT    1001
#define IDC_DIR_BROWSE  1002
#define IDC_PATH_CHK    1003
#define IDC_PROGRESS    1004
#define IDC_STATUS      1005
#define IDC_LAUNCH_CHK  1006

////////////////////////////////
// Wizard pages

static INT_PTR CALLBACK welcome_proc(HWND dlg, UINT msg, WPARAM wp, LPARAM lp) {
    switch (msg) {
    case WM_INITDIALOG:
        return TRUE;
    case WM_NOTIFY: {
        NMHDR *nm = (NMHDR *)lp;
        if (nm->code == PSN_SETACTIVE) {
            PropSheet_SetWizButtons(GetParent(dlg), PSWIZB_NEXT);
            SetWindowLongPtrW(dlg, DWLP_MSGRESULT, 0);
        }
        return TRUE;
    }
    }
    return FALSE;
}

static INT_PTR CALLBACK dir_proc(HWND dlg, UINT msg, WPARAM wp, LPARAM lp) {
    switch (msg) {
    case WM_INITDIALOG:
        SetDlgItemTextW(dlg, IDC_DIR_EDIT, g_install_dir);
        CheckDlgButton(dlg, IDC_PATH_CHK, g_add_to_path ? BST_CHECKED : BST_UNCHECKED);
        return TRUE;
    case WM_COMMAND:
        if (LOWORD(wp) == IDC_DIR_BROWSE) {
            BROWSEINFOW bi = {0};
            bi.hwndOwner = dlg;
            bi.lpszTitle = L"Select installation directory:";
            bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
            LPITEMIDLIST pidl = SHBrowseForFolderW(&bi);
            if (pidl) {
                wchar_t path[MAX_PATH];
                SHGetPathFromIDListW(pidl, path);
                wcscat_s(path, MAX_PATH, L"\\codeMAX");
                SetDlgItemTextW(dlg, IDC_DIR_EDIT, path);
                CoTaskMemFree(pidl);
            }
        }
        return TRUE;
    case WM_NOTIFY: {
        NMHDR *nm = (NMHDR *)lp;
        if (nm->code == PSN_SETACTIVE)
            PropSheet_SetWizButtons(GetParent(dlg), PSWIZB_BACK | PSWIZB_NEXT);
        if (nm->code == PSN_WIZNEXT) {
            GetDlgItemTextW(dlg, IDC_DIR_EDIT, g_install_dir, MAX_PATH);
            g_add_to_path = (IsDlgButtonChecked(dlg, IDC_PATH_CHK) == BST_CHECKED);
        }
        return TRUE;
    }
    }
    return FALSE;
}

static B32 do_install(HWND dlg) {
    HWND prog = GetDlgItem(dlg, IDC_PROGRESS);
    HWND status = GetDlgItem(dlg, IDC_STATUS);
    SendMessageW(prog, PBM_SETRANGE, 0, MAKELPARAM(0, 7));
    SendMessageW(prog, PBM_SETSTEP, 1, 0);

    // 1. Create install directory
    SetWindowTextW(status, L"Creating install directory...");
    create_directory_recursive(g_install_dir);
    SendMessageW(prog, PBM_STEPIT, 0, 0);

    // 2. Extract terminal exe
    SetWindowTextW(status, L"Installing codemax.exe...");
    wchar_t path[MAX_PATH];
    _snwprintf(path, MAX_PATH, L"%s\\codemax.exe", g_install_dir);
    if (!extract_resource(IDR_EXE_GUI, path)) {
        MessageBoxW(dlg, L"Failed to extract codemax.exe", L"Error", MB_ICONERROR);
        return 0;
    }
    SendMessageW(prog, PBM_STEPIT, 0, 0);

    // 4. Copy self as uninstaller
    SetWindowTextW(status, L"Creating uninstaller...");
    wchar_t self_path[MAX_PATH];
    GetModuleFileNameW(NULL, self_path, MAX_PATH);
    _snwprintf(path, MAX_PATH, L"%s\\uninstall.exe", g_install_dir);
    CopyFileW(self_path, path, FALSE);
    SendMessageW(prog, PBM_STEPIT, 0, 0);

    // 5. Add to PATH (optional)
    if (g_add_to_path) {
        SetWindowTextW(status, L"Updating PATH...");
        path_add(g_install_dir);
    }
    SendMessageW(prog, PBM_STEPIT, 0, 0);

    // 6. Create Start Menu shortcuts
    SetWindowTextW(status, L"Creating shortcuts...");
    CoInitialize(NULL);
    {
        wchar_t programs[MAX_PATH];
        SHGetFolderPathW(NULL, CSIDL_COMMON_PROGRAMS, NULL, 0, programs);
        wchar_t menu_dir[MAX_PATH];
        _snwprintf(menu_dir, MAX_PATH, L"%s\\codeMAX", programs);
        CreateDirectoryW(menu_dir, NULL);

        wchar_t exe_path[MAX_PATH];
        _snwprintf(exe_path, MAX_PATH, L"%s\\codemax.exe", g_install_dir);

        wchar_t lnk[MAX_PATH];
        _snwprintf(lnk, MAX_PATH, L"%s\\codeMAX.lnk", menu_dir);
        create_shortcut(lnk, exe_path, g_install_dir,
                        L"codeMAX Text Editor", exe_path);
    }
    CoUninitialize();
    SendMessageW(prog, PBM_STEPIT, 0, 0);

    // 7. Register in Add/Remove Programs
    SetWindowTextW(status, L"Registering application...");
    {
        HKEY key;
        RegCreateKeyExA(HKEY_LOCAL_MACHINE, CODEMAX_UNINSTALL_KEY,
                        0, NULL, 0, KEY_WRITE, NULL, &key, NULL);

        char dir_a[MAX_PATH], uninst[MAX_PATH * 2];
        WideCharToMultiByte(CP_UTF8, 0, g_install_dir, -1, dir_a, MAX_PATH, NULL, NULL);
        snprintf(uninst, sizeof(uninst), "\"%s\\uninstall.exe\" /uninstall", dir_a);

        RegSetValueExA(key, "DisplayName", 0, REG_SZ,
                       (BYTE *)CODEMAX_DISPLAY_NAME, (DWORD)strlen(CODEMAX_DISPLAY_NAME) + 1);
        RegSetValueExA(key, "DisplayVersion", 0, REG_SZ,
                       (BYTE *)CODEMAX_VERSION, (DWORD)strlen(CODEMAX_VERSION) + 1);
        RegSetValueExA(key, "Publisher", 0, REG_SZ,
                       (BYTE *)CODEMAX_PUBLISHER, (DWORD)strlen(CODEMAX_PUBLISHER) + 1);
        RegSetValueExA(key, "InstallLocation", 0, REG_SZ,
                       (BYTE *)dir_a, (DWORD)strlen(dir_a) + 1);
        RegSetValueExA(key, "UninstallString", 0, REG_SZ,
                       (BYTE *)uninst, (DWORD)strlen(uninst) + 1);

        char icon[MAX_PATH + 4];
        snprintf(icon, sizeof(icon), "%s\\codemax.exe,0", dir_a);
        RegSetValueExA(key, "DisplayIcon", 0, REG_SZ,
                       (BYTE *)icon, (DWORD)strlen(icon) + 1);

        DWORD one = 1;
        RegSetValueExA(key, "NoModify", 0, REG_DWORD, (BYTE *)&one, sizeof(one));
        RegSetValueExA(key, "NoRepair", 0, REG_DWORD, (BYTE *)&one, sizeof(one));

        RegCloseKey(key);
    }
    SendMessageW(prog, PBM_STEPIT, 0, 0);

    broadcast_env_change();
    SetWindowTextW(status, L"Installation complete.");
    return 1;
}

static INT_PTR CALLBACK progress_proc(HWND dlg, UINT msg, WPARAM wp, LPARAM lp) {
    switch (msg) {
    case WM_INITDIALOG:
        return TRUE;
    case WM_NOTIFY: {
        NMHDR *nm = (NMHDR *)lp;
        if (nm->code == PSN_SETACTIVE) {
            // Disable all buttons during install
            PropSheet_SetWizButtons(GetParent(dlg), 0);
            // Run install
            if (do_install(dlg)) {
                PropSheet_SetWizButtons(GetParent(dlg), PSWIZB_NEXT);
            } else {
                PropSheet_SetWizButtons(GetParent(dlg), PSWIZB_BACK);
            }
            SetWindowLongPtrW(dlg, DWLP_MSGRESULT, 0);
        }
        return TRUE;
    }
    }
    return FALSE;
}

static INT_PTR CALLBACK finish_proc(HWND dlg, UINT msg, WPARAM wp, LPARAM lp) {
    switch (msg) {
    case WM_INITDIALOG:
        CheckDlgButton(dlg, IDC_LAUNCH_CHK, BST_CHECKED);
        return TRUE;
    case WM_NOTIFY: {
        NMHDR *nm = (NMHDR *)lp;
        if (nm->code == PSN_SETACTIVE) {
            PropSheet_SetWizButtons(GetParent(dlg), PSWIZB_FINISH);
        }
        if (nm->code == PSN_WIZFINISH) {
            if (IsDlgButtonChecked(dlg, IDC_LAUNCH_CHK) == BST_CHECKED) {
                wchar_t exe[MAX_PATH];
                _snwprintf(exe, MAX_PATH, L"%s\\codemax.exe", g_install_dir);
                ShellExecuteW(NULL, L"open", exe, NULL, g_install_dir, SW_SHOWNORMAL);
            }
        }
        return TRUE;
    }
    }
    return FALSE;
}

////////////////////////////////
// Wizard launcher

static void run_installer(void) {
    // Default install dir
    wcscpy_s(g_install_dir, MAX_PATH, L"C:\\Program Files\\codeMAX");

    InitCommonControls();

    // Build page templates in memory
    DlgBuf bufs[4] = {0};
    S32 page_w = 317, page_h = 143;

    // Page 0: Welcome
    DLGTEMPLATE *dt0 = build_page_template(&bufs[0], page_w, page_h);
    add_control(&bufs[0], dt0, SS_LEFT, 10, 10, 297, 40, -1,
                L"Static", L"Welcome to the codeMAX installer.\n\n"
                L"This will install codeMAX on your computer.");
    add_control(&bufs[0], dt0, SS_LEFT, 10, 60, 297, 20, -1,
                L"Static", L"Click Next to continue.");

    // Page 1: Directory + options
    DLGTEMPLATE *dt1 = build_page_template(&bufs[1], page_w, page_h);
    add_control(&bufs[1], dt1, SS_LEFT, 10, 10, 297, 10, -1,
                L"Static", L"Choose the installation directory:");
    add_control(&bufs[1], dt1, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP,
                10, 28, 230, 14, IDC_DIR_EDIT, L"Edit", L"");
    add_control(&bufs[1], dt1, BS_PUSHBUTTON | WS_TABSTOP,
                248, 27, 60, 14, IDC_DIR_BROWSE, L"Button", L"Browse...");
    add_control(&bufs[1], dt1, BS_AUTOCHECKBOX | WS_TABSTOP,
                10, 52, 250, 14, IDC_PATH_CHK, L"Button", L"Add to system PATH");

    // Page 2: Progress
    DLGTEMPLATE *dt2 = build_page_template(&bufs[2], page_w, page_h);
    add_control(&bufs[2], dt2, SS_LEFT, 10, 10, 297, 10, IDC_STATUS,
                L"Static", L"Installing...");
    add_control(&bufs[2], dt2, 0, 10, 30, 297, 14, IDC_PROGRESS,
                PROGRESS_CLASSW, L"");

    // Page 3: Finish
    DLGTEMPLATE *dt3 = build_page_template(&bufs[3], page_w, page_h);
    add_control(&bufs[3], dt3, SS_LEFT, 10, 10, 297, 20, -1,
                L"Static", L"codeMAX has been installed successfully.");
    add_control(&bufs[3], dt3, BS_AUTOCHECKBOX | WS_TABSTOP,
                10, 45, 200, 14, IDC_LAUNCH_CHK, L"Button", L"Launch codeMAX GUI");

    PROPSHEETPAGEW pages[4] = {0};
    for (S32 i = 0; i < 4; i++) {
        pages[i].dwSize = sizeof(PROPSHEETPAGEW);
        pages[i].dwFlags = PSP_DLGINDIRECT;
        pages[i].hInstance = g_hinst;
    }
    pages[0].pResource = dt0;
    pages[0].pfnDlgProc = welcome_proc;
    pages[1].pResource = dt1;
    pages[1].pfnDlgProc = dir_proc;
    pages[2].pResource = dt2;
    pages[2].pfnDlgProc = progress_proc;
    pages[3].pResource = dt3;
    pages[3].pfnDlgProc = finish_proc;

    PROPSHEETHEADERW psh = {0};
    psh.dwSize = sizeof(PROPSHEETHEADERW);
    psh.dwFlags = PSH_WIZARD | PSH_PROPSHEETPAGE | PSH_USEICONID;
    psh.hwndParent = NULL;
    psh.hInstance = g_hinst;
    psh.pszIcon = MAKEINTRESOURCEW(IDI_CODEMAX);
    psh.pszCaption = L"codeMAX Setup";
    psh.nPages = 4;
    psh.ppsp = pages;

    PropertySheetW(&psh);
}

////////////////////////////////
// Uninstaller

static void run_uninstaller(void) {
    // Get install dir from our own exe path
    wchar_t self[MAX_PATH];
    GetModuleFileNameW(NULL, self, MAX_PATH);
    wcscpy_s(g_install_dir, MAX_PATH, self);
    wchar_t *last_sep = wcsrchr(g_install_dir, L'\\');
    if (last_sep) *last_sep = 0;

    S32 result = MessageBoxW(NULL,
        L"Are you sure you want to uninstall codeMAX?",
        L"codeMAX Uninstall", MB_YESNO | MB_ICONQUESTION);
    if (result != IDYES) return;

    // Delete installed files
    wchar_t path[MAX_PATH];
    _snwprintf(path, MAX_PATH, L"%s\\codemax.exe", g_install_dir);
    DeleteFileW(path);
    _snwprintf(path, MAX_PATH, L"%s\\codemax.exe", g_install_dir);
    DeleteFileW(path);

    // Remove from PATH
    path_remove(g_install_dir);
    broadcast_env_change();

    // Remove Start Menu shortcuts
    {
        wchar_t programs[MAX_PATH];
        SHGetFolderPathW(NULL, CSIDL_COMMON_PROGRAMS, NULL, 0, programs);
        wchar_t menu_dir[MAX_PATH];
        _snwprintf(menu_dir, MAX_PATH, L"%s\\codeMAX", programs);

        _snwprintf(path, MAX_PATH, L"%s\\codeMAX.lnk", menu_dir);
        DeleteFileW(path);
        _snwprintf(path, MAX_PATH, L"%s\\codeMAX GUI.lnk", menu_dir);
        DeleteFileW(path);
        RemoveDirectoryW(menu_dir);
    }

    // Remove registry entry
    RegDeleteKeyA(HKEY_LOCAL_MACHINE, CODEMAX_UNINSTALL_KEY);

    // Schedule self-deletion and remove install dir
    // Use cmd.exe to wait for us to exit, then delete
    wchar_t cmd[MAX_PATH * 3];
    _snwprintf(cmd, ArrayCount(cmd),
               L"cmd.exe /c timeout /t 2 /nobreak >nul & del \"%s\\uninstall.exe\" & rmdir \"%s\"",
               g_install_dir, g_install_dir);

    STARTUPINFOW si = {0};
    si.cb = sizeof(si);
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;
    PROCESS_INFORMATION pi = {0};
    CreateProcessW(NULL, cmd, NULL, NULL, FALSE,
                   CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    MessageBoxW(NULL, L"codeMAX has been uninstalled.", L"codeMAX Uninstall",
                MB_OK | MB_ICONINFORMATION);
}

////////////////////////////////
// Entry point

int main(int argc, char **argv) {
    (void)argc;
    g_hinst = GetModuleHandleW(NULL);

    // Check for uninstall flag
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "/uninstall") == 0) {
            run_uninstaller();
            return 0;
        }
    }

    // Also check via GetCommandLineW for when launched without CRT argc/argv
    wchar_t *cmdline = GetCommandLineW();
    if (wcsstr(cmdline, L"/uninstall")) {
        run_uninstaller();
        return 0;
    }

    run_installer();
    return 0;
}
