/*
 * tngp/tngstui.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 TUI launcher for TirNanoG Server
 *
 */

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <termios.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include "files.h"
#include "tngs.h"

#ifndef IFF_LOOPBACK
#define IFF_LOOPBACK	0x8
#endif
int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
int lstat(const char *path, struct stat *buf);

/**
 * Run the server in a terminal
 */
int main_runtui(char *argv)
{
    int i;
    struct stat st = { 0 };
    static char *terminals[] = {
        "/usr/bin/st",
        "/usr/bin/urxvt",
        "/usr/bin/xterm",
        "/usr/bin/kitty",
        "/usr/bin/gnome-terminal",
        "/usr/bin/konsole",
        "/usr/bin/cool-retro-term",
        NULL };

    for(i = 0; terminals[i] && lstat(terminals[i], &st); i++);
    if(terminals[i])
        execlp(terminals[i], terminals[i], "-T", "TirNanoG Server", "-e", argv, "--tui", NULL);
    /* no means to report error if we couldn't start a terminal, so just exit with an error code */
    return 1;
}

/**
 * Text User Interface server launcher
 */
int main_tui()
{
    files_game_t *list = NULL;
    struct timespec tv;
    struct termios otio, ntio;
    struct ifaddrs *ifaddr, *ifa;
    int flg, i, j, col = 0, row = 0, x, y, fld = 0, popup = 0, cur = 0, num = 0, sel = 0, scr;
    struct winsize ws;
    char c, d, ip[INET6_ADDRSTRLEN], p[8], *inp, gfn[PATH_MAX + FILENAME_MAX], sfn[PATH_MAX + FILENAME_MAX];

    /* get our IP */
    strcpy(ip, "127.0.0.1"); strcpy(p, "4433");
    if(getifaddrs(&ifaddr) != -1) {
        for(ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
            if((ifa->ifa_flags & IFF_LOOPBACK) || !ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET6) continue;
        if(!ifa)
            for(ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
                if((ifa->ifa_flags & IFF_LOOPBACK) || !ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET) continue;
        if(ifa) {
            inet_ntop(ifa->ifa_addr->sa_family, ifa->ifa_addr->sa_family == AF_INET ?
                (void*)&((struct sockaddr_in*)ifa->ifa_addr)->sin_addr : (void*)&((struct sockaddr_in6*)ifa->ifa_addr)->sin6_addr,
                ip, sizeof(ip));
        }
        freeifaddrs(ifaddr);
    }
    inp = ip; cur = strlen(ip);

    /* get list of game files */
    list = files_getgames(&num);

    /* set up console */
    flg = fcntl(0, F_GETFL);
    fcntl(0, F_SETFL, flg | O_NONBLOCK);
    tcgetattr(0, &otio);
    ntio = otio;
    ntio.c_lflag &= (~ICANON & ~ECHO);
    tcsetattr(0, TCSANOW, &ntio);
    printf("\033]0;TirNanoG Server\007\033[?25l");

    /* do the TUI stuff */
    do {
        /* redraw screen */
        ioctl(0, TIOCGWINSZ, &ws); if(row != ws.ws_row || col != ws.ws_col) printf("\033[36;44;1m\033[H\033[J");
        row = ws.ws_row; col = ws.ws_col; y = (row - 7) / 2 + 1; x = (col - 68) / 2 + 1;
        printf("\033[36;44;1m\033[H TirNanoG Server Launcher\n ");
        for(i = 0; i < col-2; i++) printf("\xe2\x94\x80");
        printf("\033[%u;%uH\033[%sm", y, x, popup ? "34;100" : "30;47");
        for(i = 0; i < 68; i++) printf(" ");
        printf("\033[%u;%uH  Bind IP \033[%sm", y + 1, x, !fld ? "37;44" : "30;46");
        for(i = 0; i < 48; i++) printf(" ");
        printf("\033[%u;%uH%s\033[%u;%uH\033[%sm:\033[%sm       \033[7D%s\033[%u;%uH\033[%sm  \033[37;40m  \033[%u;%uH\033[%sm",
            y + 1, x + 10, ip, y + 1, x + 58, popup ? "34;100" : "30;47", fld == 1 ? "37;44" : "30;46", p, y + 1, x + 66,
            popup ? "34;100" : "30;47", y + 2, x, popup ? "34;100" : "30;47");
        for(i = 0; i < 68; i++) printf(" ");
        printf("\033[37;40m  \033[%u;%uH\033[%sm  World   \033[%sm", y + 3, x, popup ? "34;100" : "30;47", fld == 2 ? "37;44" : "30;46");
        for(i = 0; i < 53; i++) printf(" ");
        printf("\033[%u;%uH%s\033[%u;%uH\033[%sm[v]  \033[37;40m  \033[%u;%uH\033[%sm", y + 3, x + 10, list ? list[sel].name : "",
            y + 3, x + 63, popup ? "34;100" : "30;47", y + 4, x, popup ? "34;100" : "30;47");
        for(i = 0; i < 68; i++) printf(" ");
        printf("\033[37;40m  \033[%u;%uH\033[%sm", y + 5, x, popup ? "34;100" : "30;47");
        for(i = 0; i < 25; i++) printf(" ");
        printf("\033[%sm < Start Server > \033[%sm", fld == 3 ? "93;44" : "30;47", popup ? "34;100" : "30;47");
        for(i = 0; i < 25; i++) printf(" ");
        printf("\033[37;40m  \033[%u;%uH\033[%sm", y + 6, x, popup ? "34;100" : "30;47");
        for(i = 0; i < 68; i++) printf(" ");
        printf("\033[37;40m  \033[%u;%uH", y + 7, x + 2);
        for(i = 0; i < 68; i++) printf(" ");
        if(popup) {
            printf("\033[%u;%uH\033[30;47m", 3, x + 8);
            for(i = 0; i < 57; i++) printf(" ");
            scr = sel > row - 7 ? sel - (row - 7) : 0;
            for(j = 0; j < row - 6; j++) {
                printf("\033[%u;%uH\033[30;47m  \033[%sm", j + 4, x + 8, scr + j == sel ? "37;44" : "30;46");
                for(i = 0; i < 53; i++) printf(" ");
                printf("\033[53D%s\033[%u;%uH\033[30;47m  \033[37;40m  ", list && scr + j < num ? list[scr + j].name : "", j + 4, x + 63);
            }
            printf("\033[30;47m\033[%u;%uH", j + 4, x + 8);
            for(i = 0; i < 57; i++) printf(" ");
            printf("\033[37;40m  \033[%u;%uH", j + 5, x + 10);
            for(i = 0; i < 57; i++) printf(" ");
        }
        switch(fld) {
            case 0: printf("\033[%u;%uH\033[1;33;42m%c\033[D", y + 1, x + 10 + cur, inp && inp[cur] ? inp[cur] : ' '); break;
            case 1: printf("\033[%u;%uH\033[1;33;42m%c\033[D", y + 1, x + 59 + cur, inp && inp[cur] ? inp[cur] : ' '); break;
        }

        /* get user events */
        do {
            c = getchar();
            if(c == 0x1b) {
                c = getchar();
                if(c == -1) { c = 0x1b; }
                else {
                    while(c != -1) { d = c; c = getchar(); }
                    switch(d) {
                        case 'A': c = 1; break;
                        case 'B': c = 2; break;
                        case 'C': c = 3; break;
                        case 'D': c = 4; break;
                        default: c = 0; break;
                    }
                }
            }
            ioctl(0, TIOCGWINSZ, &ws); if(row != ws.ws_row || col != ws.ws_col) { c = 18 /* Ctrl + R */; break; }
            tv.tv_sec = 0; tv.tv_nsec = 10000000;
            nanosleep(&tv, NULL);
        } while(c == -1);

        switch(c) {
            case 8: case 127: if(fld < 2 && inp && cur > 0) { cur--; strcpy(inp + cur, inp + cur + 1); } break;
            case 3:
                if(fld == 2) goto up;
                if(fld < 2 && inp && inp[cur]) cur++;
            break;
            case 4:
                if(fld == 2) goto down;
                if(fld < 2 && inp && cur > 0) cur--;
            break;
            case '.': case ':': if(!fld) goto add; break;
            case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
add:             if(fld < 2 && inp) { memmove(inp + cur + 1, inp + cur, strlen(inp + cur) + 1); inp[cur++] = c; }
            break;
            case 1:
                if(popup) {
up:                 if(sel > 0) sel--;
                } else {
                    fld--; if(fld < 0) fld = 3;
                    goto setfld;
                }
            break;
            case 2:
                if(popup) {
down:               if(sel + 1 < num) sel++;
                } else {
                    fld++; if(fld > 3) fld = 0;
                    goto setfld;
                }
            break;
            case 9:
                fld++; if(fld > 3) fld = 0;
setfld:         switch(fld) {
                    case 0: inp = ip; cur = strlen(ip); break;
                    case 1: inp = p; cur = strlen(p); break;
                    default: inp = NULL; break;
                }
            break;
            case 10:
                switch(fld) {
                    case 0: case 1: fld++; goto setfld;
                    case 2: if(!popup) { popup = 1; } else { popup = row = 0; fld++; } break;
                    case 3:
                        if(!list || sel >= num || !ip[0]) break;
                        tcsetattr(0, TCSANOW, &otio);
                        fcntl(0, F_SETFL, flg);
                        printf("\033[0;37;40m\033[H\033[J");
                        bindip = ip; port = p;
                        strcpy(gfn, list[sel].url); main_fn = gfn;
                        files_init(); sprintf(sfn, "%s/%s.sav", files_saves, list[sel].gameid); savefile = sfn; files_free();
                        files_freegames(&list, &num);
                        foreground = verbose = tui = 1;
                        main_run();
                        printf("\r\nPress Enter to close the window..."); fflush(stdout);
                        getchar();
                        return 0;
                    break;
                }
            break;
            case 18: row = 0; break;
            case 27: if(popup) { popup = row = c = 0; continue; } break;
        }
    } while(c!=0x1b);
    tcsetattr(0, TCSANOW, &otio);
    fcntl(0, F_SETFL, flg);
    return 0;
}

