#include <SDL2/SDL.h>
#include "video.h"
#include "wx-sdl2-video.h"
#include "wx-utils.h"
#include "ibm.h"
#include "wx-display.h"
#include "plat-keyboard.h"

#ifdef __WINDOWS__
#define BITMAP WINDOWS_BITMAP
#undef UNICODE
#include <windows.h>
#include <windowsx.h>
#undef BITMAP

HHOOK hKeyboardHook;
int modkeystate[255];

#endif

SDL_mutex *rendererMutex;
SDL_cond *rendererCond;
SDL_Thread *renderthread = NULL;

SDL_Window *window = NULL;

int rendering = 0;

int mousecapture = 0;

extern int pause;
extern int video_scale;
extern int take_screenshot;

void *ghwnd;
void *menu;

SDL_Rect remembered_rect;
int remembered_mouse_x = 0;
int remembered_mouse_y = 0;

int custom_resolution_width = 640;
int custom_resolution_height = 480;

int win_doresize = 0;
int winsizex = 640, winsizey = 480;

void renderer_start();
void renderer_stop(int timeout);

int trigger_fullscreen = 0;
int trigger_screenshot = 0;
int trigger_togglewindow = 0;
int trigger_inputrelease = 0;

extern void device_force_redraw();
extern void mouse_wheel_update(int);
extern void toggle_fullscreen();

void display_resize(int width, int height) {
        winsizex = width * (video_scale + 1) >> 1;
        winsizey = height * (video_scale + 1) >> 1;

        SDL_Rect rect;
        rect.x = rect.y = 0;
        rect.w = winsizex;
        rect.h = winsizey;
        sdl_scale(video_fullscreen_scale, rect, &rect, winsizex, winsizey);
        winsizex = rect.w;
        winsizey = rect.h;

        win_doresize = 1;
}

void releasemouse() {
        if (mousecapture) {
                SDL_SetWindowGrab(window, SDL_FALSE);
                SDL_SetRelativeMouseMode(SDL_FALSE);
                mousecapture = 0;
        }
}

int display_init() {
        if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) {
                printf("SDL could not initialize! Error: %s\n", SDL_GetError());
                return 0;
        }

        SDL_version ver;
        SDL_GetVersion(&ver);
        printf("SDL %i.%i.%i initialized.\n", ver.major, ver.minor, ver.patch);
        return 1;
}

void display_close() { SDL_Quit(); }

void display_start(void *hwnd) {
        ghwnd = hwnd;
        menu = wx_getmenu(hwnd);
        atexit(releasemouse);
        rendererMutex = SDL_CreateMutex();
        rendererCond = SDL_CreateCond();
        renderer_start();
}

void display_stop() {
        renderer_stop(10 * 1000);

        SDL_DestroyMutex(rendererMutex);
        SDL_DestroyCond(rendererCond);
#if SDL_VERSION_ATLEAST(2, 0, 2)
        SDL_DetachThread(renderthread);
#endif
        releasemouse();
}

int is_fullscreen() {
        int flags = SDL_GetWindowFlags(window);
        return (flags & SDL_WINDOW_FULLSCREEN) || (flags & SDL_WINDOW_FULLSCREEN_DESKTOP);
}

void sdl_set_window_title(const char *title) {
        if (window && !is_fullscreen())
                SDL_SetWindowTitle(window, title);
}

int get_border_size(int *top, int *left, int *bottom, int *right) {
#if SDL_VERSION_ATLEAST(2, 0, 5)
        return SDL_GetWindowBordersSize(window, top, left, bottom, right);
#else
        if (top)
                *top = 0;
        if (left)
                *left = 0;
        if (bottom)
                *bottom = 0;
        if (right)
                *right = 0;
        return 0;
#endif
}

static const struct {
        SDL_Scancode sdl;
        int system;
} SDLScancodeToSystemScancode[] = {{SDL_SCANCODE_A, 0x1e},
                                   {SDL_SCANCODE_B, 0x30},
                                   {SDL_SCANCODE_C, 0x2e},
                                   {SDL_SCANCODE_D, 0x20},
                                   {SDL_SCANCODE_E, 0x12},
                                   {SDL_SCANCODE_F, 0x21},
                                   {SDL_SCANCODE_G, 0x22},
                                   {SDL_SCANCODE_H, 0x23},
                                   {SDL_SCANCODE_I, 0x17},
                                   {SDL_SCANCODE_J, 0x24},
                                   {SDL_SCANCODE_K, 0x25},
                                   {SDL_SCANCODE_L, 0x26},
                                   {SDL_SCANCODE_M, 0x32},
                                   {SDL_SCANCODE_N, 0x31},
                                   {SDL_SCANCODE_O, 0x18},
                                   {SDL_SCANCODE_P, 0x19},
                                   {SDL_SCANCODE_Q, 0x10},
                                   {SDL_SCANCODE_R, 0x13},
                                   {SDL_SCANCODE_S, 0x1f},
                                   {SDL_SCANCODE_T, 0x14},
                                   {SDL_SCANCODE_U, 0x16},
                                   {SDL_SCANCODE_V, 0x2f},
                                   {SDL_SCANCODE_W, 0x11},
                                   {SDL_SCANCODE_X, 0x2d},
                                   {SDL_SCANCODE_Y, 0x15},
                                   {SDL_SCANCODE_Z, 0x2c},
                                   {SDL_SCANCODE_0, 0x0B},
                                   {SDL_SCANCODE_1, 0x02},
                                   {SDL_SCANCODE_2, 0x03},
                                   {SDL_SCANCODE_3, 0x04},
                                   {SDL_SCANCODE_4, 0x05},
                                   {SDL_SCANCODE_5, 0x06},
                                   {SDL_SCANCODE_6, 0x07},
                                   {SDL_SCANCODE_7, 0x08},
                                   {SDL_SCANCODE_8, 0x09},
                                   {SDL_SCANCODE_9, 0x0A},
                                   {SDL_SCANCODE_GRAVE, 0x29},
                                   {SDL_SCANCODE_MINUS, 0x0c},
                                   {SDL_SCANCODE_EQUALS, 0x0d},
                                   {SDL_SCANCODE_NONUSBACKSLASH, 0x56},
                                   {SDL_SCANCODE_BACKSLASH, 0x2b},
                                   {SDL_SCANCODE_BACKSPACE, 0x0e},
                                   {SDL_SCANCODE_SPACE, 0x39},
                                   {SDL_SCANCODE_TAB, 0x0f},
                                   {SDL_SCANCODE_CAPSLOCK, 0x3a},
                                   {SDL_SCANCODE_LSHIFT, 0x2a},
                                   {SDL_SCANCODE_LCTRL, 0x1d},
                                   {SDL_SCANCODE_LGUI, 0xdb},
                                   {SDL_SCANCODE_LALT, 0x38},
                                   {SDL_SCANCODE_RSHIFT, 0x36},
                                   {SDL_SCANCODE_RCTRL, 0x9d},
                                   {SDL_SCANCODE_RGUI, 0xdc},
                                   {SDL_SCANCODE_RALT, 0xb8},
                                   {SDL_SCANCODE_SYSREQ, 0x54},
                                   {SDL_SCANCODE_APPLICATION, 0xdd},
                                   {SDL_SCANCODE_RETURN, 0x1c},
                                   {SDL_SCANCODE_ESCAPE, 0x01},
                                   {SDL_SCANCODE_F1, 0x3B},
                                   {SDL_SCANCODE_F2, 0x3C},
                                   {SDL_SCANCODE_F3, 0x3D},
                                   {SDL_SCANCODE_F4, 0x3e},
                                   {SDL_SCANCODE_F5, 0x3f},
                                   {SDL_SCANCODE_F6, 0x40},
                                   {SDL_SCANCODE_F7, 0x41},
                                   {SDL_SCANCODE_F8, 0x42},
                                   {SDL_SCANCODE_F9, 0x43},
                                   {SDL_SCANCODE_F10, 0x44},
                                   {SDL_SCANCODE_F11, 0x57},
                                   {SDL_SCANCODE_F12, 0x58},
                                   {SDL_SCANCODE_SCROLLLOCK, 0x46},
                                   {SDL_SCANCODE_LEFTBRACKET, 0x1a},
                                   {SDL_SCANCODE_RIGHTBRACKET, 0x1b},
                                   {SDL_SCANCODE_INSERT, 0xd2},
                                   {SDL_SCANCODE_HOME, 0xc7},
                                   {SDL_SCANCODE_PAGEUP, 0xc9},
                                   {SDL_SCANCODE_DELETE, 0xd3},
                                   {SDL_SCANCODE_END, 0xcf},
                                   {SDL_SCANCODE_PAGEDOWN, 0xd1},
                                   {SDL_SCANCODE_UP, 0xc8},
                                   {SDL_SCANCODE_LEFT, 0xcb},
                                   {SDL_SCANCODE_DOWN, 0xd0},
                                   {SDL_SCANCODE_RIGHT, 0xcd},
                                   {SDL_SCANCODE_NUMLOCKCLEAR, 0x45},
                                   {SDL_SCANCODE_KP_DIVIDE, 0xb5},
                                   {SDL_SCANCODE_KP_MULTIPLY, 0x37},
                                   {SDL_SCANCODE_KP_MINUS, 0x4a},
                                   {SDL_SCANCODE_KP_PLUS, 0x4e},
                                   {SDL_SCANCODE_KP_ENTER, 0x9c},
                                   {SDL_SCANCODE_KP_PERIOD, 0x53},
                                   {SDL_SCANCODE_KP_0, 0x52},
                                   {SDL_SCANCODE_KP_1, 0x4f},
                                   {SDL_SCANCODE_KP_2, 0x50},
                                   {SDL_SCANCODE_KP_3, 0x51},
                                   {SDL_SCANCODE_KP_4, 0x4b},
                                   {SDL_SCANCODE_KP_5, 0x4c},
                                   {SDL_SCANCODE_KP_6, 0x4d},
                                   {SDL_SCANCODE_KP_7, 0x47},
                                   {SDL_SCANCODE_KP_8, 0x48},
                                   {SDL_SCANCODE_KP_9, 0x49},
                                   {SDL_SCANCODE_SEMICOLON, 0x27},
                                   {SDL_SCANCODE_APOSTROPHE, 0x28},
                                   {SDL_SCANCODE_COMMA, 0x33},
                                   {SDL_SCANCODE_PERIOD, 0x34},
                                   {SDL_SCANCODE_SLASH, 0x35},
                                   {SDL_SCANCODE_PRINTSCREEN, 0xb7}};

int sdl_scancode(SDL_Scancode scancode) {
        int i;
        for (i = 0; i < SDL_arraysize(SDLScancodeToSystemScancode); ++i) {
                if (SDLScancodeToSystemScancode[i].sdl == scancode) {
                        return SDLScancodeToSystemScancode[i].system;
                }
        }
        return -1;
}

SDL_Event event;
SDL_Rect rect;
int border_x, border_y = 0;

uint64_t render_time = 0;
int render_fps = 0;
uint32_t render_frame_time = 0;
uint32_t render_frames = 0;

void window_setup() {
        SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1");
#if SDL_VERSION_ATLEAST(2, 0, 5)
        SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
#endif

        if (start_in_fullscreen) {
                start_in_fullscreen = 0;
                window_dofullscreen = 1;
                window_doinputgrab = 1;
        }

        if (window_remember) {
                rect.x = window_x;
                rect.y = window_y;
                rect.w = window_w;
                rect.h = window_h;
        } else {
                rect.x = SDL_WINDOWPOS_CENTERED;
                rect.y = SDL_WINDOWPOS_CENTERED;
                rect.w = 640;
                rect.h = 480;
        }

        if (vid_resize == 2) {
                rect.w = custom_resolution_width;
                rect.h = custom_resolution_height;
        }
}

#ifdef __WINDOWS__
int sdl_winhook(int code) {
        switch (code) {
        case VK_LMENU:
                return SDL_SCANCODE_LALT;
        case VK_LCONTROL:
                return SDL_SCANCODE_LCTRL;
        case VK_LWIN:
                return SDL_SCANCODE_LGUI;
        case VK_LSHIFT:
                return SDL_SCANCODE_LSHIFT;
        case VK_RMENU:
                return SDL_SCANCODE_RALT;
        case VK_RCONTROL:
                return SDL_SCANCODE_RCTRL;
        case VK_RWIN:
                return SDL_SCANCODE_RGUI;
        case VK_RSHIFT:
                return SDL_SCANCODE_RSHIFT;
        }

        return -1;
}

LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
        KBDLLHOOKSTRUCT *p = (KBDLLHOOKSTRUCT *)lParam;

        int c = p->vkCode;

        int steal = 0;
        int s = sdl_winhook(c);
        if (s != -1) {
                int key_state = !(p->flags & LLKHF_UP);
                if (abs(modkeystate[c]) != key_state) {
                        /* if mousecapture is 0, key_state should be negative */
                        if (!mousecapture)
                                key_state = -key_state;
                        /* if key_state is 0 and modkeystate[c] is negative,
                         an sdl_event should not be generated */
                        if (key_state > 0 || modkeystate[c] > 0) {
                                steal = key_state != 0;
                                SDL_Event event;
                                event.key.keysym.scancode = s;
                                event.key.timestamp = p->time;

                                event.type = key_state ? SDL_KEYDOWN : SDL_KEYUP;
                                SDL_PushEvent(&event);
                        }
                        modkeystate[c] = key_state;
                }
                if (steal)
                        return 1;
        }

        return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}
#endif

int window_create() {
        window = SDL_CreateWindow("PCem Display", rect.x, rect.y, rect.w, rect.h,
                                  requested_render_driver.sdl_window_params | (vid_resize == 1 ? SDL_WINDOW_RESIZABLE : 0));
        if (!window) {
                char message[200];
                sprintf(message, "SDL window could not be created! Error: %s\n", SDL_GetError());
                wx_messagebox(ghwnd, message, "SDL Error", WX_MB_OK);
                return 0;
        }

#ifdef __WINDOWS__
        memset(modkeystate, 0, sizeof(modkeystate));
        hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0);
#endif

        render_time = 0;
        render_fps = 0;
        render_frame_time = SDL_GetTicks();
        render_frames = 0;
        return 1;
}

void window_close() {
#ifdef __WINDOWS__
        UnhookWindowsHookEx(hKeyboardHook);
#endif

        sdl_renderer_close();

        if (window) {
                SDL_GetWindowPosition(window, &rect.x, &rect.y);
                SDL_GetWindowSize(window, &rect.w, &rect.h);
                get_border_size(&border_y, &border_x, 0, 0);
                rect.x -= border_x;
                rect.y -= border_y;

                SDL_DestroyWindow(window);
        }
        window = NULL;
}

int render() {
        uint64_t start_time = timer_read();
        uint64_t end_time;

        if (window_dosetresize) {
                window_dosetresize = 0;
#if SDL_VERSION_ATLEAST(2, 0, 5)
                SDL_GetWindowSize(window, &rect.w, &rect.h);
                SDL_SetWindowResizable(window, vid_resize == 1);
                SDL_SetWindowSize(window, rect.w, rect.h);
#else
                window_doreset = 1;
#endif
                if (vid_resize == 2)
                        SDL_SetWindowSize(window, custom_resolution_width, custom_resolution_height);

                device_force_redraw();
        }
        if (window_doreset) {
                pclog("window_doreset\n");
                window_doreset = 0;
                renderer_doreset = 0;
                return 0;
        }
        if (renderer_doreset) {
                pclog("renderer_doreset\n");
                renderer_doreset = 0;
                sdl_renderer_close();
                sdl_renderer_init(window);

                device_force_redraw();
                video_wait_for_blit();
        }
        while (SDL_PollEvent(&event)) {
                switch (event.type) {
                case SDL_MOUSEBUTTONUP:
                        if (!mousecapture) {
                                if (event.button.button == SDL_BUTTON_LEFT && !pause) {
                                        window_doinputgrab = 1;
                                        if (video_fullscreen)
                                                window_dofullscreen = 1;
                                } else if (event.button.button == SDL_BUTTON_RIGHT)
                                        wx_popupmenu(ghwnd, menu, 0, 0);

                        } else if (event.button.button == SDL_BUTTON_MIDDLE && !is_fullscreen())
                                window_doinputrelease = 1;
                        break;
                case SDL_MOUSEWHEEL:
                        if (mousecapture)
                                mouse_wheel_update(event.wheel.y);
                        break;
                case SDL_WINDOWEVENT:
                        if (event.window.event == SDL_WINDOWEVENT_CLOSE)
                                wx_stop_emulation(ghwnd);
                        if (event.window.event == SDL_WINDOWEVENT_RESIZED)
                                device_force_redraw();
                        if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) {
                                if (is_fullscreen())
                                        window_dowindowed = 1;
                                window_doinputrelease = 1;
                        }

                        if (window_remember) {
                                int flags = SDL_GetWindowFlags(window);
                                if (!(flags & SDL_WINDOW_FULLSCREEN) && !(flags & SDL_WINDOW_FULLSCREEN_DESKTOP)) {
                                        if (event.window.event == SDL_WINDOWEVENT_MOVED) {
                                                get_border_size(&border_y, &border_x, 0, 0);
                                                window_x = event.window.data1 - border_x;
                                                window_y = event.window.data2 - border_y;
                                        } else if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
                                                window_w = event.window.data1;
                                                window_h = event.window.data2;
                                        }
                                        // save_window_pos = 1;
                                }
                        }

                        break;
                case SDL_KEYDOWN: {
#ifdef __WINDOWS__
                        /* international keyboard workaround */
                        if (event.key.keysym.scancode == SDL_SCANCODE_RALT && (event.key.keysym.mod & KMOD_LCTRL) &&
                            event.key.timestamp == rawinputkey[sdl_scancode(SDL_SCANCODE_LCTRL)])
                                rawinputkey[sdl_scancode(SDL_SCANCODE_LCTRL)] = 0;
#endif
                        int key_idx = sdl_scancode(event.key.keysym.scancode);
                        if (key_idx != -1)
                                rawinputkey[key_idx] = event.key.timestamp;
                        break;
                }
                case SDL_KEYUP: {
                        int key_idx = sdl_scancode(event.key.keysym.scancode);
                        if (key_idx != -1)
                                rawinputkey[key_idx] = 0;
                        break;
                }
                }
        }
        if ((rawinputkey[sdl_scancode(SDL_SCANCODE_PAGEDOWN)] || rawinputkey[sdl_scancode(SDL_SCANCODE_KP_3)]) &&
            (rawinputkey[sdl_scancode(SDL_SCANCODE_LCTRL)] || rawinputkey[sdl_scancode(SDL_SCANCODE_RCTRL)]) &&
            (rawinputkey[sdl_scancode(SDL_SCANCODE_LALT)] || rawinputkey[sdl_scancode(SDL_SCANCODE_RALT)]))
                trigger_fullscreen = 1;
        else if (trigger_fullscreen) {
                trigger_fullscreen = 0;
                toggle_fullscreen();
        } else if ((rawinputkey[sdl_scancode(SDL_SCANCODE_PAGEUP)] || rawinputkey[sdl_scancode(SDL_SCANCODE_KP_9)]) &&
                   (rawinputkey[sdl_scancode(SDL_SCANCODE_LCTRL)] || rawinputkey[sdl_scancode(SDL_SCANCODE_RCTRL)]) &&
                   (rawinputkey[sdl_scancode(SDL_SCANCODE_LALT)] || rawinputkey[sdl_scancode(SDL_SCANCODE_RALT)]))
                trigger_screenshot = 1;
        else if (trigger_screenshot) {
                trigger_screenshot = 0;
                take_screenshot = 1;
        } else if ((rawinputkey[sdl_scancode(SDL_SCANCODE_END)] || rawinputkey[sdl_scancode(SDL_SCANCODE_KP_1)]) &&
                   (rawinputkey[sdl_scancode(SDL_SCANCODE_LCTRL)] || rawinputkey[sdl_scancode(SDL_SCANCODE_RCTRL)]))
                trigger_inputrelease = 1;
        else if (trigger_inputrelease) {
                trigger_inputrelease = 0;
                if (!is_fullscreen())
                        window_doinputrelease = 1;
        }
        if (window_doremember) {
                window_doremember = 0;
                SDL_GetWindowPosition(window, &window_x, &window_y);
                SDL_GetWindowSize(window, &window_w, &window_h);
                get_border_size(&border_y, &border_x, 0, 0);
                window_x -= border_x;
                window_y -= border_y;
                saveconfig(NULL);
        }

        if (window_dotogglefullscreen) {
                window_dotogglefullscreen = 0;
                if (SDL_GetWindowGrab(window) || is_fullscreen()) {
                        window_doinputrelease = 1;
                        if (is_fullscreen())
                                window_dowindowed = 1;
                } else {
                        window_doinputgrab = 1;
                        window_dofullscreen = 1;
                }
        }

        if (window_dofullscreen) {
                window_dofullscreen = 0;
                video_wait_for_blit();
                SDL_RaiseWindow(window);
#if SDL_VERSION_ATLEAST(2, 0, 4)
                SDL_GetGlobalMouseState(&remembered_mouse_x, &remembered_mouse_y);
#endif
                SDL_GetWindowPosition(window, &remembered_rect.x, &remembered_rect.y);
                get_border_size(&border_y, &border_x, 0, 0);
                remembered_rect.x -= border_x;
                remembered_rect.y -= border_y;
                SDL_GetWindowSize(window, &remembered_rect.w, &remembered_rect.h);
                SDL_SetWindowFullscreen(window,
                                        video_fullscreen_mode == 0 ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN);
                device_force_redraw();
        }
        if (window_doinputgrab) {
                window_doinputgrab = 0;
                mousecapture = 1;
                SDL_GetRelativeMouseState(0, 0);
                SDL_SetWindowGrab(window, SDL_TRUE);
                SDL_SetRelativeMouseMode(SDL_TRUE);
        }

        if (window_doinputrelease) {
                window_doinputrelease = 0;
                mousecapture = 0;
                SDL_SetWindowGrab(window, SDL_FALSE);
                SDL_SetRelativeMouseMode(SDL_FALSE);
        }
        if (window_dowindowed) {
                window_dowindowed = 0;
                SDL_SetWindowFullscreen(window, 0);
                SDL_SetWindowSize(window, remembered_rect.w, remembered_rect.h);
                SDL_SetWindowPosition(window, remembered_rect.x, remembered_rect.y);
#if SDL_VERSION_ATLEAST(2, 0, 4)
                SDL_WarpMouseGlobal(remembered_mouse_x, remembered_mouse_y);
#endif
                device_force_redraw();
        }

        if (win_doresize) {
                int flags = SDL_GetWindowFlags(window);

                win_doresize = 0;
                if (!vid_resize || (flags & SDL_WINDOW_FULLSCREEN)) {
                        SDL_GetWindowSize(window, &rect.w, &rect.h);
                        if (rect.w != winsizex || rect.h != winsizey) {
                                SDL_GetWindowPosition(window, &rect.x, &rect.y);
                                SDL_SetWindowSize(window, winsizex, winsizey);
                                SDL_SetWindowPosition(window, rect.x, rect.y);
                                device_force_redraw();
                        }
                }
        }

        if (sdl_renderer_update(window))
                sdl_renderer_present(window);

        end_time = timer_read();
        render_time += end_time - start_time;

        ++render_frames;
        uint32_t ticks = SDL_GetTicks();
        if (ticks - render_frame_time >= 1000) {
                render_fps = render_frames / ((ticks - render_frame_time) / 1000.0);
                render_frames = 0;
                render_frame_time = ticks;
        }

        return 1;
}

int renderer_thread(void *params) {
        int internal_rendering;

        SDL_LockMutex(rendererMutex);
        SDL_CondSignal(rendererCond);
        SDL_UnlockMutex(rendererMutex);

        window_setup();

        rendering = 1;
        while (rendering) {

                if (!window_create())
                        rendering = 0;

                renderer_doreset = 1;
                internal_rendering = 1;
                pclog ("Start rendering...\n");
                while (rendering && internal_rendering) {
                        if (!render())
                                internal_rendering = 0;
                    
                        SDL_Delay(1);
                }
                pclog ("Stop rendering...\n");
                window_close();
        }

        SDL_LockMutex(rendererMutex);
        SDL_CondSignal(rendererCond);
        SDL_UnlockMutex(rendererMutex);

        return SDL_TRUE;
}

void *timer = 0;

void render_timer() {
#ifdef PCEM_RENDER_TIMER_LOOP
        /* For some reason this while-loop works on OSX, which also fixes missing events. No idea why though. */
        renderer_thread(0);
#else
        if (rendering && !render()) {
                window_close();
                window_create();
                renderer_doreset = 1;
        }
#endif
}

void render_start_timer() {
#ifdef PCEM_RENDER_TIMER_LOOP
        timer = wx_createtimer(render_timer);
        wx_starttimer(timer, 500, 1);
#else
        window_setup();
        if (window_create()) {
                rendering = 1;
                renderer_doreset = 1;
                wx_starttimer(timer, 1, 0);
        }
#endif
}

void renderer_start() {
        if (!rendering) {
#ifdef PCEM_RENDER_WITH_TIMER
                render_start_timer();
#else
                SDL_LockMutex(rendererMutex);
                renderthread = SDL_CreateThread(renderer_thread, "SDL2 Thread", NULL);
                SDL_CondWait(rendererCond, rendererMutex);
                SDL_UnlockMutex(rendererMutex);
#endif
        }
}

void renderer_stop(int timeout) {
#if defined(PCEM_RENDER_WITH_TIMER) && defined(PCEM_RENDER_TIMER_LOOP)
        rendering = 0;
        window_close();
        wx_destroytimer(timer);
#else
        if (rendering) {
                SDL_LockMutex(rendererMutex);
                rendering = 0;
                if (timeout)
                        SDL_CondWaitTimeout(rendererCond, rendererMutex, timeout);
                else
                        SDL_CondWait(rendererCond, rendererMutex);
                SDL_UnlockMutex(rendererMutex);
                renderthread = NULL;
        }
        if (timer)
                wx_destroytimer(timer);
#endif
}
