#include <SDL2/SDL.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "video.h"
#include "wx-utils.h"
#include "wx-sdl2-video.h"
#include "wx-sdl2-video-renderer.h"

static SDL_Texture *texture = NULL;
static SDL_Renderer *renderer = NULL;

extern int video_scale_mode;
extern int video_vsync;
extern int video_focus_dim;
extern int take_screenshot;
extern void screenshot_taken(unsigned char *rgb, int width, int height);

int sdl_video_renderer_init(SDL_Window *window, sdl_render_driver requested_render_driver, SDL_Rect screen) {
        SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, video_scale_mode ? "1" : "0");
        const char *driver = requested_render_driver.sdl_id;
        if (!driver)
                driver = "0";
        SDL_SetHint(SDL_HINT_RENDER_DRIVER, driver);

        int flags = SDL_RENDERER_ACCELERATED;
        if (video_vsync) {
                flags |= SDL_RENDERER_PRESENTVSYNC;
        }
        renderer = SDL_CreateRenderer(window, -1, flags);

        if (!renderer) {
                char message[200];
                sprintf(message, "SDL window could not be created! Error: %s\n", SDL_GetError());
                wx_messagebox(0, message, "SDL Error", WX_MB_OK);
                return SDL_FALSE;
        }

        SDL_RendererInfo rendererInfo;
        SDL_GetRendererInfo(renderer, &rendererInfo);
        sdl_render_driver *d = sdl_get_render_driver_by_name_ptr(rendererInfo.name);
        if (!d)
                strcpy(current_render_driver_name, "Unknown");
        else
                strcpy(current_render_driver_name, d->name);

        texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, screen.w, screen.h);

        return SDL_TRUE;
}

void sdl_video_renderer_close() {
        if (texture) {
                SDL_DestroyTexture(texture);
                texture = NULL;
        }
        if (renderer) {
                SDL_DestroyRenderer(renderer);
                renderer = NULL;
        }
}

void sdl_video_renderer_update(SDL_Window *window, SDL_Rect updated_rect, VIDEO_BITMAP *screen) {
        SDL_UpdateTexture(texture, &updated_rect, &((uint32_t *)screen->dat)[updated_rect.y * screen->w + updated_rect.x],
                          screen->w * 4);
}

void sdl_video_renderer_present(SDL_Window *window, SDL_Rect texture_rect, SDL_Rect window_rect, SDL_Rect screen) {
        SDL_RenderClear(renderer);
        SDL_RenderCopy(renderer, texture, &texture_rect, &window_rect);
        int sshot = take_screenshot;
        if (!sshot) {
                if (video_focus_dim && !(SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS)) {
                        SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
                        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0x80);
                        SDL_RenderFillRect(renderer, NULL);
                        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xff);
                        SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
                }
                if (flash.enabled) {
                        SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
                        SDL_SetRenderDrawColor(renderer, flash.color[0], flash.color[1], flash.color[2], flash.color[3]);
                        SDL_RenderFillRect(renderer, NULL);
                        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xff);
                        SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
                }
        }
        SDL_RenderPresent(renderer);

        if (sshot) {
                take_screenshot = 0;

                int width = window_rect.w;
                int height = window_rect.h;

                SDL_GetWindowSize(window, &width, &height);

                /* seems to work without rendering to texture first */
                //                SDL_Texture* tex = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
                //                SDL_TEXTUREACCESS_TARGET, width, height);
                //
                //                SDL_SetRenderTarget(renderer, tex);
                //                SDL_RenderClear(renderer);
                //                SDL_RenderCopy(renderer, texture, &texture_rect, &window_rect);
                //                SDL_RenderPresent(renderer);

                unsigned char *rgba = (unsigned char *)malloc(width * height * 4);
                int res = SDL_RenderReadPixels(renderer, NULL, SDL_PIXELFORMAT_ABGR8888, rgba, width * 4);

                //                SDL_SetRenderTarget(renderer, NULL);
                //                SDL_DestroyTexture(tex);

                if (!res) {
                        int x, y;
                        unsigned char *rgb = (unsigned char *)malloc(width * height * 3);

                        for (x = 0; x < width; ++x) {
                                for (y = 0; y < height; ++y) {
                                        rgb[(y * width + x) * 3 + 0] = rgba[(y * width + x) * 4 + 0];
                                        rgb[(y * width + x) * 3 + 1] = rgba[(y * width + x) * 4 + 1];
                                        rgb[(y * width + x) * 3 + 2] = rgba[(y * width + x) * 4 + 2];
                                }
                        }

                        screenshot_taken(rgb, width, height);

                        free(rgb);
                } else
                        screenshot_taken(0, 0, 0);

                free(rgba);
        }
}

sdl_renderer_t *sdl2_renderer_create() {
        sdl_renderer_t *renderer = malloc(sizeof(sdl_renderer_t));
        renderer->init = sdl_video_renderer_init;
        renderer->close = sdl_video_renderer_close;
        renderer->update = sdl_video_renderer_update;
        renderer->present = sdl_video_renderer_present;
        renderer->always_update = 0;
        return renderer;
}

void sdl2_renderer_close(sdl_renderer_t *renderer) { free(renderer); }

int sdl2_renderer_available(struct sdl_render_driver *driver) {
        int i;
        SDL_RendererInfo renderInfo;
        for (i = 0; i < SDL_GetNumRenderDrivers(); ++i) {
                SDL_GetRenderDriverInfo(i, &renderInfo);
                if (!strcmp(driver->sdl_id, renderInfo.name))
                        return 1;
        }
        return 0;
}
