/* Copyright 2022 Jaakko Keränen <jaakko.keranen@iki.fi>

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

#include "video.h"
#include "render.h"
#include "events.h"
#include "SDL_timer.h"
#include "SDL_keyboard.h"

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <the_Foundation/stringlist.h>
#include <the_Foundation/process.h>
#include <the_Foundation/vec2.h>

SDL_Window *currentWindow_Video     = NULL;

iBool       disableColors_Video     = iFalse;
iBool       isColored_Video         = iTrue;
iBool       isFixedVGAPalette_Video = iFalse;
iBool       isSimpleCharMode_Video  = iFalse;

static iBlock *runSystemCommand_(const char *cmd) {
    iBlock *result = new_Block(0);
    FILE *p = popen(cmd, "r");
    if (!p) return result;
    for (;;) {
        int c = fgetc(p);
        if (c == EOF) break;
        pushBack_Block(result, (char) c);
    }
    pclose(p);
    return result;
}

iInt2 SDL_QueryTerminalSize(void) {
    /* TODO: Curses may provide ROWS and COLS; check environment, too. */
    iBlock *output = runSystemCommand_("stty size");
    iInt2 size = zero_I2();
    sscanf(cstr_Block(output), "%d %d", &size.y, &size.x);
    if (isEqual_I2(size, zero_I2())) {
        return init_I2(80, 24);
    }
    return size;
}

static void postControlC_(int s) {
    SDL_SendKeyboardPress(SDLK_c, KMOD_CTRL);
}

static void postQuit_(int s) {
    SDL_PushEvent(&(SDL_Event){ .type = SDL_QUIT });
}

int SDL_GetNumVideoDisplays(void) {
    return 1;
}

void SDL_EnableScreenSaver(void) {}

static void windowResized_(int sig) {
    iUnused(sig);
    ungetch(KEY_RESIZE);
}

static void SDL_PushWindowEvent(SDL_Window *d, int winEvent) {
    SDL_WindowEvent ev = { .type      = SDL_WINDOWEVENT,
                           .timestamp = SDL_GetTicks(),
                           .event     = winEvent };
    SDL_PushEvent((SDL_Event *) &ev);
}

SDL_Window *SDL_CreateWindow(const char *title, int x, int y, int w, int h, int flags) {
    iInt2 size = SDL_QueryTerminalSize();
    signal(SIGINT, postControlC_);
    signal(SIGQUIT, postQuit_);
    signal(SIGWINCH, windowResized_);
    SDL_Window *d = calloc(sizeof(SDL_Window), 1);
    WINDOW *cwin = initscr();
    if (!disableColors_Video) {
        start_color();
        isFixedVGAPalette_Video = !can_change_color() || COLORS < 64;
        isColored_Video = (has_colors() ? iTrue : iFalse);
    }
    else {
        isColored_Video = iFalse;
    }
    d->cwin = cwin;
    d->flags = flags;
    curs_set(0);
    leaveok(cwin, true);
    nodelay(cwin, true);
    keypad(cwin, true);
    scrollok(cwin, false);
    raw();
    noecho();
    cbreak();
    nonl();
    wbkgdset(cwin, 0);
    werase(cwin);
    wrefresh(cwin);
    d->w = size.x;
    d->h = size.y;
    currentWindow_Video = d;
    if (~d->flags & SDL_WINDOW_HIDDEN) {
        SDL_PushWindowEvent(d, SDL_WINDOWEVENT_EXPOSED);
        SDL_PushWindowEvent(d, SDL_WINDOWEVENT_SHOWN);
    }
    return d;
}

void SDL_DestroyWindow(SDL_Window *d) {
    if (d) {
        curs_set(1);
        nocbreak();
        scrollok(d->cwin, true);
        keypad(d->cwin, false);
        nodelay(d->cwin, false);
        noraw();
        echo();
        nl();
        endwin();
        free(d);
    }
    currentWindow_Video = NULL;
}

uint32_t SDL_GetWindowID(SDL_Window *d) {
    return 0;
}

void SDL_ShowWindow(SDL_Window *d) {
    if (d->flags & SDL_WINDOW_HIDDEN) {
        d->flags &= ~SDL_WINDOW_HIDDEN;
        SDL_PushWindowEvent(d, SDL_WINDOWEVENT_EXPOSED);
        SDL_PushWindowEvent(d, SDL_WINDOWEVENT_SHOWN);
    }
}

void SDL_HideWindow(SDL_Window *d) {
    if (~d->flags & SDL_WINDOW_HIDDEN) {
        d->flags |= SDL_WINDOW_HIDDEN;
    }
}

void SDL_MaximizeWindow(SDL_Window *d) {}

void SDL_MinimizeWindow(SDL_Window *d) {}

void SDL_RestoreWindow(SDL_Window *d) {}

void SDL_RaiseWindow(SDL_Window *d) {}

void SDL_GetWindowSize(SDL_Window *d, int *w, int *h) {
    if (w) *w = d->w;
    if (h) *h = d->h;
}

uint32_t SDL_GetWindowFlags(SDL_Window *d) {
    return 0;
}

void SDL_SetWindowPosition(SDL_Window *d, int x, int y) {
    iUnused(d, x, y);
}

void SDL_SetWindowSize(SDL_Window *d, int w, int h) {
    iUnused(d, w, h);
}

void SDL_SetWindowMinimumSize(SDL_Window *d, int w, int h) {
    iUnused(d, w, h);
}

int SDL_GetWindowDisplayIndex(SDL_Window *d) {
    return 0;
}

void SDL_GetWindowPosition(SDL_Window *d, int *x, int *y) {
    iUnused(d);
    if (x) *x = 0;
    if (y) *y = 0;
}

void SDL_GetDisplayBounds(int index, SDL_Rect *bounds) {
    if (index == 0) {
        *bounds = (SDL_Rect){ 0, 0, currentWindow_Video->w, currentWindow_Video->h };
    }
}

void SDL_GetDisplayUsableBounds(int index, SDL_Rect *bounds) {
    if (index == 0) {
        *bounds = (SDL_Rect){ 0, 0, currentWindow_Video->w, currentWindow_Video->h };
    }
}

void SDL_SetWindowTitle(SDL_Window *d, const char *title) {

}

void SDL_SetWindowFullscreen(SDL_Window *d, int mode) {

}

void SDL_SetWindowInputFocus(SDL_Window *d) {

}

void SDL_ResizeWindow(SDL_Window *d, int w, int h) {
    d->w = w;
    d->h = h;
    SDL_UpdateRendererToWindowSize();
}
