#include "display.h"
#include <cstring>
#include "sm83.h"
#include "ppu.h"
#include <SDL2/SDL.h>
#include "ui_window.h"
#include "debug.h"
#include "ui.h"

namespace display
{
    uint32_t palette[2][4] = {
        { 0xFFFFFF, 0xAAAAAA, 0x555555, 0x000000 },
        { 0xA0AA05, 0x749527, 0x487F49, 0x2E4326/*0x1D6A6B*/ }
    };
    SDL_Window *win = nullptr;
    SDL_Renderer *ren = nullptr;
    SDL_Texture *tex = nullptr;
    SDL_Texture *uitex = nullptr;

    uint8_t zoom = 3;
    uint8_t use_palette=1;
    bool fullscreen = false;
    int fullscreen_scale = 1;
    SDL_Rect dest_rect;

    bool eventHandler(SDL_Event *e)
    {
        if (e->type==SDL_WINDOWEVENT) {
            if (e->window.event==SDL_WINDOWEVENT_CLOSE) {
                return false;
            } else if ((e->window.event==SDL_WINDOWEVENT_SHOWN) || (e->window.event==SDL_WINDOWEVENT_EXPOSED)) {
                redraw();
            }
        }
        if (!debug::debugging()) {
            if (debug::paused()) {
                if (e->type == SDL_KEYDOWN) {
                    if (e->key.keysym.scancode==SDL_SCANCODE_ESCAPE) {
                        const uint8_t dt = sm83::step();
                        debug::cont();
                        ppu::refresh(dt);
                    }
                }
            } else {
                if (e->type == SDL_KEYDOWN) {
                    if (e->key.keysym.scancode==SDL_SCANCODE_ESCAPE) {
                        debug::pause();
                        display::redraw();
                    } else if (e->key.keysym.scancode==SDL_SCANCODE_F1) {
                        display::decZoom();
                    } else if (e->key.keysym.scancode==SDL_SCANCODE_F2) {
                        display::incZoom();
                    } else if (e->key.keysym.scancode==SDL_SCANCODE_F3) {
                        display::toggleFullscreen();
                    } else if (e->key.keysym.scancode==SDL_SCANCODE_Q && e->key.keysym.mod & KMOD_CTRL) {
                        return false;
                    }
                }
            }
        }
        if (e->type == SDL_MOUSEMOTION) {
            SDL_ShowCursor(true);
        }
        return true;
    }

    void init()
    {
        if (win) ui::window::unregisterWindow(SDL_GetWindowID(win));

        if (tex) SDL_DestroyTexture(tex);
        if (uitex) SDL_DestroyTexture(uitex);
        if (ren) SDL_DestroyRenderer(ren);
        if (win) SDL_DestroyWindow(win);

        const int z = fullscreen ? 1 : zoom;
        win = SDL_CreateWindow("Gameboy Screen", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 160*z, 144*z, fullscreen?SDL_WINDOW_FULLSCREEN_DESKTOP:SDL_WINDOW_SHOWN);
        ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
        tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 160, 144);
        uitex = ui::createtexture(ren);

        ui::window::registerWindow(SDL_GetWindowID(win), eventHandler);

        if (fullscreen)        
        {
            int w, h;
            SDL_GetWindowSize(win, &w, &h);
            fullscreen_scale = h/144;
            dest_rect.w = 160 * fullscreen_scale;
            dest_rect.h = 144 * fullscreen_scale;
            dest_rect.x = (w - dest_rect.w)/2;
            dest_rect.y = (h - dest_rect.h)/2;
        }
        else
        {
            dest_rect.x = dest_rect.y = 0;
            dest_rect.w = 160 * zoom;
            dest_rect.h = 144 * zoom;
        }

        focus();
    }

    void focus()
    {
        if (win)
        {
            SDL_RaiseWindow(win);
            redraw();
        }
    }
    
    void redraw(const bool present)
    {
        //if (zx_tape::getplaying() && zx_tape::getOption(ZXTAPE_OPTION_FAST_LOAD)) return;

        ui::setrenderer(ren, uitex);

        const uint8_t *ppu_pixels = ppu::getpixels();
        Uint32* pixels;
        int pitch;
        SDL_LockTexture(tex, NULL, (void**)&pixels, &pitch);
        for (int i=0; i<160*144;++i) *(pixels++) = palette[use_palette][ppu_pixels[i]];
        SDL_UnlockTexture(tex);

        if (fullscreen)
        {
            SDL_SetRenderDrawColor(ren, 0, 0, 0, 255);
            SDL_RenderClear(ren);
        }

        // Pintem la textura a pantalla
        SDL_RenderCopy(ren, tex, NULL, &dest_rect);

        // Pintem les ralles dels pixels
        if (zoom>2 || fullscreen) {
            SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND);
            SDL_SetRenderDrawColor(ren, 160, 170, 5, 96);
            const float scale = fullscreen ? fullscreen_scale : zoom;
            for (int i=0;i<144;++i) SDL_RenderDrawLine(ren, dest_rect.x, dest_rect.y+i*scale, dest_rect.x+159*scale, dest_rect.y+i*scale);
            for (int i=0;i<160;++i) SDL_RenderDrawLine(ren, dest_rect.x+i*scale, dest_rect.y, dest_rect.x+i*scale, dest_rect.y+143*scale);
        }

        if (present)
            SDL_RenderPresent(ren);
        else
        {
            SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND);
            SDL_SetRenderDrawColor(ren, 0, 0, 0, 128);
            SDL_Rect rect {0,0,160*zoom,144*zoom};
            if (fullscreen) SDL_GetWindowSize(win, &rect.w, &rect.h);
            SDL_RenderFillRect(ren, &rect);
        }
    }

    void present()
    {
        SDL_RenderPresent(ren);
    }

    void setTitle(const char* title)
    {
        char tmp[256];
        strcpy(tmp, "Gameboy Screen");
        strcat(tmp, title);
        SDL_SetWindowTitle(win, tmp);
    }

    void setZoom(const int value)
    {
        if (fullscreen) return;

        if (value < 1) return;
        SDL_DisplayMode dm;
        SDL_GetCurrentDisplayMode(0, &dm);

        if (160*value > dm.w) return;
        if (144*value > dm.h) return;

        zoom = value;
        init();
    }

    void incZoom()
    {
        if (fullscreen) return;
        setZoom(zoom+1);
    }

    void decZoom()
    {
        if (fullscreen) return;
        setZoom(zoom-1);
    }

    void toggleFullscreen()
    {
        fullscreen = !fullscreen;
        init();
    }

    const bool getFullscreen()
    {
        return fullscreen;
    }
}
