/*
 * tngp/tngswin.c
 *
 * Copyright (C) 2022 bzt
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * @brief Little Windows launcher for TirNanoG Server
 *
 */

#define UNICODE 1
#define WINVER 0x0600
#ifndef _WIN32_WINNT
#define _WIN32_WINNT WINVER
#endif
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <winsock2.h>
#include <windows.h>
#include <winnls.h>
#include <fileapi.h>
#include <iphlpapi.h>
#include <ws2tcpip.h>
#include "files.h"
#include "tngs.h"

int _getch();

/* create a console */
void makeconsole()
{
    FILE *f;
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    HANDLE console;
    COORD bs;

    AllocConsole();
    console = GetStdHandle(STD_OUTPUT_HANDLE);
    f = _fdopen(_open_osfhandle((intptr_t)console, 0x4000/*_O_TEXT*/), "w");
    if(f) {
        setvbuf(f, NULL, _IONBF, 0);
        SetConsoleTitleA("TirNanoG Server");
        if(GetConsoleScreenBufferInfo(console, &csbi)) { bs.X = 132; bs.Y = 32767; SetConsoleScreenBufferSize(console, bs); }
        *stdout = *f;
        *stderr = *f;
    } else
        exit(1);
}

/* these two functions were borrowed from sdl_windows_main.c */
static void UnEscapeQuotes(char *arg)
{
    char *last = NULL, *c_curr, *c_last;

    while (*arg) {
        if (*arg == '"' && (last != NULL && *last == '\\')) {
            c_curr = arg;
            c_last = last;

            while (*c_curr) {
                *c_last = *c_curr;
                c_last = c_curr;
                c_curr++;
            }
            *c_last = '\0';
        }
        last = arg;
        arg++;
    }
}

/* Parse a command line buffer into arguments */
static int ParseCommandLine(WCHAR *cmdline, char **argv)
{
    char *bufp, *args;
    char *lastp = NULL;
    int argc, last_argc, l;

    l = WideCharToMultiByte(CP_UTF8, 0, cmdline, -1, NULL, 0, NULL, NULL);
    if(!l || !(args = malloc(l))) return 0;
    WideCharToMultiByte(CP_UTF8, 0, cmdline, -1, args, l, NULL, NULL);
    argc = last_argc = 0;
    for (bufp = args; *bufp;) {
        /* Skip leading whitespace */
        while (*bufp == ' ') {
            ++bufp;
        }
        /* Skip over argument */
        if (*bufp == '"') {
            ++bufp;
            if (*bufp) {
                if (argv) {
                    argv[argc] = bufp;
                }
                ++argc;
            }
            /* Skip over word */
            lastp = bufp;
            while (*bufp && (*bufp != '"' || *lastp == '\\')) {
                lastp = bufp;
                ++bufp;
            }
        } else {
            if (*bufp) {
                if (argv) {
                    argv[argc] = bufp;
                }
                ++argc;
            }
            /* Skip over word */
            while (*bufp && *bufp != ' ') {
                ++bufp;
            }
        }
        if (*bufp) {
            if (argv) {
                *bufp = '\0';
            }
            ++bufp;
        }

        /* Strip out \ from \" sequences */
        if (argv && last_argc != argc) {
            UnEscapeQuotes(argv[last_argc]);
        }
        last_argc = argc;
    }
    if (argv) {
        argv[argc] = NULL;
    }
    return (argc);
}

/**
 * Event dispatcher
 */
static files_game_t *list = NULL;
static int num = 0;
static INT_PTR CALLBACK MainDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    char ip[INET6_ADDRSTRLEN], p[FILENAME_MAX], gfn[MAX_PATH + FILENAME_MAX], sfn[MAX_PATH + FILENAME_MAX];
    int i;
    PIP_ADAPTER_ADDRESSES pAddresses = NULL, addr = NULL;
    PIP_ADAPTER_UNICAST_ADDRESS ua;
    DWORD ret = 0;
    ULONG len = 16384;
    WCHAR path[MAX_PATH + FILENAME_MAX];

    switch (uMsg) {
        case WM_INITDIALOG:
            SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)LoadIcon((HINSTANCE)lParam, MAKEINTRESOURCE(11)));
            memset(ip, 0, sizeof(ip)); strcpy(ip, "127.0.0.1");
            /* get our IP */
            ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, NULL, &len);
            if(ret == ERROR_BUFFER_OVERFLOW) {
                pAddresses = (IP_ADAPTER_ADDRESSES *)malloc(len);
                if(pAddresses) {
                    ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &len);
                    if(ret == ERROR_SUCCESS) {
                        for(addr = pAddresses; addr != NULL && addr->IfType == MIB_IF_TYPE_LOOPBACK; addr = addr->Next);
                        if(!addr) addr = pAddresses;
                        for(ua = addr->FirstUnicastAddress; ua != NULL && ua->Address.lpSockaddr->sa_family != AF_INET6; ua = ua->Next);
                        if(!ua) ua = addr->FirstUnicastAddress;
                        inet_ntop(ua->Address.lpSockaddr->sa_family, ua->Address.lpSockaddr->sa_family == AF_INET ?
                            (void*)&((struct sockaddr_in*)ua->Address.lpSockaddr)->sin_addr :
                            (void*)&((struct sockaddr_in6*)ua->Address.lpSockaddr)->sin6_addr,
                            ip, sizeof(ip));
                    }
                    free(pAddresses);
                }
            }
            SetDlgItemTextA(hwndDlg, 103, ip);
            SetDlgItemTextA(hwndDlg, 105, "4433");
            /* get list of game files */
            files_init(); list = files_getgames(&num); files_free();
            for(i = 0; list && i < num; i++) {
                MultiByteToWideChar(CP_UTF8, 0, list[i].name, -1, path, PATH_MAX);
                SendDlgItemMessageW(hwndDlg, 107, CB_ADDSTRING, 0, (LPARAM)path);
            }
            SendDlgItemMessageW(hwndDlg, 107, CB_SETCURSEL, 0, 0);
        return TRUE;
        case WM_CLOSE: EndDialog(hwndDlg, 0); return TRUE;
        case WM_COMMAND:
            if(LOWORD(wParam) == 108 && HIWORD(wParam) == BN_CLICKED) {
                GetDlgItemTextA(hwndDlg, 103, ip, sizeof(ip)); bindip = ip;
                if(!ip[0]) return FALSE;
                i = (int)SendMessage(GetDlgItem(hwndDlg, 107), CB_GETCURSEL, 0, 0);
                if(i < 0 || i >= num) return FALSE;
                strcpy(gfn, list[i].url); main_fn = gfn;
                files_init(); sprintf(sfn, "%s\\%s.sav", files_saves, list[i].gameid); savefile = sfn; files_free();
                files_freegames(&list, &num);
                GetDlgItemTextA(hwndDlg, 105, p, sizeof(p)); port = p;
                EndDialog(hwndDlg, 0);
                makeconsole();
                foreground = verbose = tui = 1;
                main_run();
                printf("\r\nPress Enter to close the window..."); fflush(stdout);
                _getch();
                return TRUE;
            }
            return FALSE;
        default: return FALSE;
    }
}

/**
 * Windows entry point
 */
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    FILE *f = NULL;
    WCHAR *cmdline = GetCommandLineW();
    int ret = 0, argc = ParseCommandLine(cmdline, NULL);
    HANDLE console;
    char **argv;
    (void)hPrevInstance;
    (void)lpCmdLine;
    (void)nCmdShow;

    AttachConsole(ATTACH_PARENT_PROCESS);
    console = GetStdHandle(STD_OUTPUT_HANDLE);
    if(console) f = _fdopen(_open_osfhandle((intptr_t)console, 0x4000/*_O_TEXT*/), "w");
    if(f || argc > 1) {
        if(!f) makeconsole();
        else { *stdout = *f; *stderr = *f; }
        argv = malloc((argc + 2) * sizeof(char*));
        ParseCommandLine(cmdline, argv);
        ret = main_cli(argc, argv);
        free(argv);
        exit(ret);
    } else
        ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(101), NULL, MainDlgProc, (LPARAM) hInstance);
    return ret;
}
