#include "platform/screen.h"

#include "city/view.h"
#include "core/calc.h"
#include "core/config.h"
#include "core/image.h"
#include "game/settings.h"
#include "game/system.h"
#include "graphics/graphics.h"
#include "graphics/menu.h"
#include "graphics/screen.h"
#include "platform/android/android.h"
#include "platform/icon.h"
#include "platform/renderer.h"
#include "platform/switch/switch.h"
#include "platform/vita/vita.h"

#include "SDL.h"

#include <stdlib.h>

static struct {
    SDL_Window *window;
} SDL;

static struct {
    int x;
    int y;
    int centered;
} window_pos = { 0, 0, 1 };

static struct {
    const int WIDTH;
    const int HEIGHT;
} MINIMUM = { 640, 480 };

static struct {
    int requested_percentage;
    int percentage;
    float screen_density;
} scale = { 100, 100, 1 };

static int scale_logical_to_pixels(int logical_value)
{
    return (int) (logical_value * scale.percentage / 100 / scale.screen_density);
}

static int scale_pixels_to_logical(int pixel_value)
{
    return (int) (pixel_value * 100 / scale.percentage / scale.screen_density);
}

static int get_max_scale_percentage(int pixel_width, int pixel_height)
{
    int width_scale_pct = (int) (pixel_width * 100 / scale.screen_density / MINIMUM.WIDTH);
    int height_scale_pct = (int) (pixel_height * 100 / scale.screen_density / MINIMUM.HEIGHT);
    return SDL_min(width_scale_pct, height_scale_pct);
}

static void apply_max_scale(int pixel_width, int pixel_height)
{
    scale.percentage = scale.requested_percentage;
    int max_scale = get_max_scale_percentage(pixel_width, pixel_height);
    if (scale.percentage > max_scale) {
        scale.percentage = max_scale;
        SDL_Log("Maximum scale of %i applied (requested: %d)", scale.percentage, scale.requested_percentage);
    }
}

static void set_scale_percentage(int new_scale, int pixel_width, int pixel_height)
{
#ifdef __vita__
    scale.requested_percentage = 100;
#else
    scale.requested_percentage = calc_bound(new_scale, 50, 500);
#endif

    if (!pixel_width || !pixel_height) {
        return;
    }

    apply_max_scale(pixel_width, pixel_height);

    SDL_SetWindowMinimumSize(SDL.window,
        scale_logical_to_pixels(MINIMUM.WIDTH), scale_logical_to_pixels(MINIMUM.HEIGHT));
}

#if !defined(_WIN32) && !defined(__APPLE__)
static void set_window_icon(void)
{
    SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(platform_icon_get_pixels(), 16, 16, 32, 16 * 4,
        0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
    if (!surface) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create surface for icon. Reason: %s", SDL_GetError());
    }
    SDL_SetWindowIcon(SDL.window, surface);
    SDL_FreeSurface(surface);
}
#endif

int platform_screen_create(const char *title, int display_scale_percentage)
{
#ifdef __ANDROID__
    scale.screen_density = android_get_screen_density();
#endif
    set_scale_percentage(display_scale_percentage, 0, 0);

    int width, height;
    int fullscreen = system_is_fullscreen_only() ? 1 : setting_fullscreen();
    if (fullscreen) {
        SDL_DisplayMode mode;
        SDL_GetDesktopDisplayMode(0, &mode);
        width = mode.w;
        height = mode.h;
    } else {
        setting_window(&width, &height);
        width = scale_logical_to_pixels(width);
        height = scale_logical_to_pixels(height);
    }

    platform_screen_destroy();

#ifdef __ANDROID__
    // Fix for wrong colors on some android devices
    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
#endif

    SDL_Log("Creating screen %d x %d, %s, driver: %s", width, height,
        fullscreen ? "fullscreen" : "windowed", SDL_GetCurrentVideoDriver());
    Uint32 flags = SDL_WINDOW_RESIZABLE;

#if SDL_VERSION_ATLEAST(2, 0, 1)
    flags |= SDL_WINDOW_ALLOW_HIGHDPI;
#endif

    if (fullscreen) {
        flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
    }

    SDL.window = SDL_CreateWindow(title,
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        width, height, flags);

    if (!SDL.window) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create window: %s", SDL_GetError());
        return 0;
    }

#if !defined(_WIN32) && !defined (__APPLE__)
    // Windows and mac don't need setting a window icon. In fact the icon gets blurry if we do
    set_window_icon();
#endif

    if (system_is_fullscreen_only()) {
        SDL_GetWindowSize(SDL.window, &width, &height);
    }

    if (!platform_renderer_init(SDL.window)) {
        return 0;
    }

#if !defined(__APPLE__)
    if (fullscreen && SDL_GetNumVideoDisplays() > 1) {
        SDL_SetWindowGrab(SDL.window, SDL_TRUE);
    }
#endif
    set_scale_percentage(display_scale_percentage, width, height);
    return platform_screen_resize(width, height, 1);
    }

void platform_screen_destroy(void)
{
    platform_renderer_destroy();
    if (SDL.window) {
        SDL_DestroyWindow(SDL.window);
        SDL.window = 0;
    }
}

int platform_screen_resize(int pixel_width, int pixel_height, int save)
{
    apply_max_scale(pixel_width, pixel_height);

    int logical_width = scale_pixels_to_logical(pixel_width);
    int logical_height = scale_pixels_to_logical(pixel_height);

    if (save) {
        setting_set_display(setting_fullscreen(), logical_width, logical_height);
    }

    if (platform_renderer_create_render_texture(logical_width, logical_height)) {
        screen_set_resolution(logical_width, logical_height);
        return 1;
    } else {
        return 0;
    }
}

int system_scale_display(int display_scale_percentage)
{
    int width, height;
    SDL_GetWindowSize(SDL.window, &width, &height);
    set_scale_percentage(display_scale_percentage, width, height);
    platform_screen_resize(width, height, 1);
    return scale.percentage;
}

int system_can_scale_display(int *min_scale, int *max_scale)
{
#ifndef __ANDROID__
    if (system_is_fullscreen_only()) {
        return 0;
    }
#endif
    int width, height;
    SDL_GetWindowSize(SDL.window, &width, &height);
#ifdef __ANDROID__
    int max_scale_current_orientation = get_max_scale_percentage(width, height);
    int max_scale_alternative_orientation = get_max_scale_percentage(height, width);
    if (max_scale_current_orientation < 100 && max_scale_alternative_orientation < 100) {
        SDL_Log("Not allowing scale on Android: %d x %d = max scale %d or %d",
            width, height, max_scale_current_orientation, max_scale_alternative_orientation);
        return 0;
    }
#endif
    if (min_scale) {
        *min_scale = 50;
    }
    if (max_scale) {
        *max_scale = get_max_scale_percentage(width, height);
    }
    return 1;
}

void platform_screen_move(int x, int y)
{
    if (!setting_fullscreen()) {
        window_pos.x = x;
        window_pos.y = y;
        window_pos.centered = 0;
    }
}

int platform_screen_get_scale(void)
{
    return scale.percentage;
}

void platform_screen_set_fullscreen(void)
{
    SDL_GetWindowPosition(SDL.window, &window_pos.x, &window_pos.y);
    int display = SDL_GetWindowDisplayIndex(SDL.window);
    SDL_DisplayMode mode;
    SDL_GetDesktopDisplayMode(display, &mode);
    SDL_Log("User to fullscreen %d x %d on display %d", mode.w, mode.h, display);
    if (0 != SDL_SetWindowFullscreen(SDL.window, SDL_WINDOW_FULLSCREEN_DESKTOP)) {
        SDL_Log("Unable to enter fullscreen: %s", SDL_GetError());
        return;
    }
    SDL_SetWindowDisplayMode(SDL.window, &mode);

#if !defined(__APPLE__)
    if (SDL_GetNumVideoDisplays() > 1) {
        SDL_SetWindowGrab(SDL.window, SDL_TRUE);
    }
#endif
    setting_set_display(1, mode.w, mode.h);
}

void platform_screen_set_windowed(void)
{
    if (system_is_fullscreen_only()) {
        return;
    }
    int logical_width, logical_height;
    setting_window(&logical_width, &logical_height);
    int pixel_width = scale_logical_to_pixels(logical_width);
    int pixel_height = scale_logical_to_pixels(logical_height);
    int display = SDL_GetWindowDisplayIndex(SDL.window);
    SDL_Log("User to windowed %d x %d on display %d", pixel_width, pixel_height, display);
    SDL_SetWindowFullscreen(SDL.window, 0);
    SDL_SetWindowSize(SDL.window, pixel_width, pixel_height);
    if (window_pos.centered) {
        platform_screen_center_window();
    }
    if (SDL_GetWindowGrab(SDL.window) == SDL_TRUE) {
        SDL_SetWindowGrab(SDL.window, SDL_FALSE);
    }
    setting_set_display(0, pixel_width, pixel_height);
}

void platform_screen_set_window_size(int logical_width, int logical_height)
{
    if (system_is_fullscreen_only()) {
        return;
    }
    int pixel_width = scale_logical_to_pixels(logical_width);
    int pixel_height = scale_logical_to_pixels(logical_height);
    int display = SDL_GetWindowDisplayIndex(SDL.window);
    if (setting_fullscreen()) {
        SDL_SetWindowFullscreen(SDL.window, 0);
    } else {
        SDL_GetWindowPosition(SDL.window, &window_pos.x, &window_pos.y);
    }
    if (SDL_GetWindowFlags(SDL.window) & SDL_WINDOW_MAXIMIZED) {
        SDL_RestoreWindow(SDL.window);
    }
    SDL_SetWindowSize(SDL.window, pixel_width, pixel_height);
    if (window_pos.centered) {
        platform_screen_center_window();
    }
    SDL_Log("User resize to %d x %d on display %d", pixel_width, pixel_height, display);
    if (SDL_GetWindowGrab(SDL.window) == SDL_TRUE) {
        SDL_SetWindowGrab(SDL.window, SDL_FALSE);
    }
    setting_set_display(0, pixel_width, pixel_height);
}

void platform_screen_center_window(void)
{
    int display = SDL_GetWindowDisplayIndex(SDL.window);
    SDL_SetWindowPosition(SDL.window,
        SDL_WINDOWPOS_CENTERED_DISPLAY(display), SDL_WINDOWPOS_CENTERED_DISPLAY(display));
    window_pos.centered = 1;
}

#ifdef _WIN32
void platform_screen_recreate_texture(void)
{
    // On Windows, if ctrl + alt + del is pressed during fullscreen, the rendering context may be lost for a few frames
    // after restoring the window, preventing the texture from being recreated. This forces an attempt to recreate the
    // texture every frame to bypass that issue.
    if (setting_fullscreen() && platform_renderer_lost_render_texture()) {
        SDL_DisplayMode mode;
        SDL_GetWindowDisplayMode(SDL.window, &mode);
        screen_set_resolution(scale_pixels_to_logical(mode.w), scale_pixels_to_logical(mode.h));
        platform_renderer_create_render_texture(screen_width(), screen_height());
    }
}
#endif

void platform_screen_show_error_message_box(const char *title, const char *message)
{
    SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, message, SDL.window);
}

void system_set_mouse_position(int *x, int *y)
{
    *x = calc_bound(*x, 0, screen_width() - 1);
    *y = calc_bound(*y, 0, screen_height() - 1);
    SDL_WarpMouseInWindow(SDL.window, scale_logical_to_pixels(*x), scale_logical_to_pixels(*y));
}

void system_change_window_title(const char *title)
{
    SDL_SetWindowTitle(SDL.window, title);
}

int system_is_fullscreen_only(void)
{
#if defined(__ANDROID__) || defined(__SWITCH__) || defined(__vita__)
    return 1;
#else
    return 0;
#endif
}

void system_get_max_resolution(int *width, int *height)
{
    SDL_DisplayMode mode;
    int index = SDL_GetWindowDisplayIndex(SDL.window);
    SDL_GetCurrentDisplayMode(index, &mode);
    *width = scale_pixels_to_logical(mode.w);
    *height = scale_pixels_to_logical(mode.h);
}
