#include "sys_vr_passthru.h"
#include "system.h"
#include "sys_desktop.h"
#include "platform.h"
#include "ui.h"
#include "sys_log.h"
#include "localisation.h"
#include "localsettings.h"
#include <stdlib.h>

static int yrDesktop_init(void* loadtoken);
static int yrDesktop_tick(void);
static int yrDesktop_save(void* savetoken, void* saveevent, int* savefailed) { yrEvent_set(saveevent, 1); return 0; }
static int yrDesktop_canfreeze(void)		{ return 1; }
static int yrDesktop_shutdown(void);
static int yrDesktop_freeze(void);
static int yrDesktop_unfreeze(void);

#define UI_MAX_AR (1.333333333333f)
#define UI_MIN_W (400)
#define UI_MIN_H (300)

void yrDesktop_reg(void)
{
	yrSystem_register(sysDesktop,
					  yrDesktop_init,
					  yrDesktop_tick,
					  yrDesktop_save,
					  yrDesktop_canfreeze,
					  yrDesktop_freeze,
					  yrDesktop_unfreeze,
					  yrDesktop_shutdown,
					  (1<<sysLog)|(1<<sysSteam)|(1<<sysPlatform));
}

/************
* Static vars
*************/
static yrWindow window = NULL;
static GLuint screenquad_vao = 0;
static GLuint screenquad_vbo = 0;
static GLuint shaderprog = 0;
extern const GLchar* FragShader_ui_screen;
static GLint uni_ui_size = -1;
static GLint uni_ui_off = -1;
static unsigned window_w = UI_MIN_W;
static unsigned window_h = UI_MIN_H;
static unsigned ui_w = UI_MIN_W;
static unsigned ui_h = UI_MIN_H;
static int ui_ready = 0;
//static int frozen = 0;

#define clamp(l,h,v) (((h)>(v))?(((v)<(l))?(l):(v)):(h))

/******
* Funcs
*******/
static void ui_mouse(void* id, int x, int y)
{
	if(!ui_ready) return;
	int off = (int)(window_w - ui_w)/2;
	x -= off;
	x = clamp(0, (int)ui_w, x);
	y = clamp(0, (int)ui_h, y);
	yrUi_mousepos(x, y);
}

static void ui_keyevent(void* id, unsigned keycode, int down)
{
	if(!ui_ready) return;
	//if(down && frozen) return;
	yrUi_keyevent(keycode, down);
}

static unsigned calculate_width(unsigned w, unsigned h)
{
	unsigned max_w = (unsigned)(h * UI_MAX_AR);
	return (w > max_w) ? max_w : w;
}

static void ui_resize(void* id, unsigned w, unsigned h)
{
	if(!ui_ready) return;
	//if(frozen) return;
	window_w = w;
	window_h = h;
	ui_h = h;
	ui_w = calculate_width(w, h);
	yrUi_resize(ui_w, ui_h);
}

static void ui_char(void* id, unsigned c)
{
	if(!ui_ready) return;
	//if(frozen) return;
	yrUi_char(c);
}


static int yrDesktop_init(void* loadtoken)
{
	int err = 0;
	//frozen = 0;

	//local settings settings (includes last window size)
	err = yrLocalSettings_init();
	if(err) { yrLog(0, "Could not load local settings, using defaults."); }
	const char* locale = NULL;
	const char* subloc = NULL;
	size_t wdata_len = 0;
	void* wdata = 0;
	yrLocalSettings_window_get(&wdata_len, &wdata);
	yrLocalSettings_locale_get(&locale, &subloc);

	err = yrLocalise_init("data/ui/uistrings", locale, subloc);
	if(err) {
		yrLog(0, "Localisation init failed.");
		return -1;
	}

	//create window
	window_w = 1280;
	window_h = 720;
	window = yrWindow_create(window_w, window_h,
							 UI_MIN_W, UI_MIN_H,
							 NULL, ui_mouse, ui_keyevent, ui_resize, ui_char);
	if(!window) {
		yrLog(0, "Window creation failed.");
		return -1;
	}
	yrWindow_set_title(window, yrLocalise_get_string("windowtitle"));
	yrWindow_set_placement(window, wdata_len, wdata, &window_w, &window_h);

	//load ui screens
	ui_h = window_h;
	ui_w = calculate_width(window_w, window_h);
	err = yrUi_load(ui_w, ui_h);
	if(err) {
		yrLog(0, "Failed to load desktop UI.");
		return -1;
	}

	//screenquad
	glGenVertexArrays(1, &screenquad_vao);
	glGenBuffers(1, &screenquad_vbo);
	glBindVertexArray(screenquad_vao);
	glBindBuffer(GL_ARRAY_BUFFER, screenquad_vbo);
	glBufferData(GL_ARRAY_BUFFER, 4 * 2 * sizeof(GLfloat), NULL, GL_STATIC_DRAW);
	glVertexAttribPointer(0, 2, GL_FLOAT, FALSE, 0, (GLvoid*)0);
	glEnableVertexAttribArray(0);

	float* buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
	if(!buf) {
		yrLog(0, "Could not map OpenGL buffer: %x", glGetError());
		return -1;
	}
	buf[0*2 + 0] = -1.0f;
	buf[0*2 + 1] = -1.0f;
	buf[1*2 + 0] = 1.0f;
	buf[1*2 + 1] = -1.0f;
	buf[2*2 + 0] = 1.0f;
	buf[2*2 + 1] = 1.0f;
	buf[3*2 + 0] = -1.0f;
	buf[3*2 + 1] = 1.0f;
	glUnmapBuffer(GL_ARRAY_BUFFER);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);
	GLenum glerr = glGetError();
	if(glerr != GL_NO_ERROR) {
		yrLog(0, "OpenGL error during ui screenquad buffer write: %x", glGetError());
		return -1;
	}

	//offset blit shader
	err = 0;
	shaderprog = glCreateProgram();
	err = OpenGL_shader(shaderprog, GL_FRAGMENT_SHADER, FragShader_ui_screen);
	if(err) { yrLog(0, "Compilation error(s) were in ui screen fragment shader."); return -1; }
	glLinkProgram(shaderprog);
	glerr = glGetError();
	if(glerr != GL_NO_ERROR) { yrLog(0, "Ui screen shaderprogram failed to link: %x", glerr); return -1; }

	uni_ui_size	= glGetUniformLocation(shaderprog, "ui_size");
	uni_ui_off	= glGetUniformLocation(shaderprog, "ui_off");
	glerr = glGetError();
	if(glerr != GL_NO_ERROR) {
		yrLog(0, "OpenGL error during screen uniform location acquisition: %x", glerr);
		return -1;
	}
	glEnable(GL_FRAMEBUFFER_SRGB);
	ui_ready = 1;
	return 0;
}

static int yrDesktop_tick(void)
{
	//update ui
	GLuint screenbuf = yrUi_renderupdate();
	
	//render screenbuffer to window
	glDisable(GL_MULTISAMPLE);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_BLEND);
	glUseProgram(shaderprog);
	glBindBuffer(GL_ARRAY_BUFFER, screenquad_vbo);
	glBindVertexArray(screenquad_vao);
	glBindTexture(GL_TEXTURE_2D, screenbuf);
	
	float ui_size[2] = {(float)ui_w, (float)ui_h};
	float ui_off[2] = {(float)((window_w - ui_w)/2), 0.0f};
	glUniform2fv(uni_ui_size, 1, ui_size);
	glUniform2fv(uni_ui_off, 1, ui_off);
	
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	glViewport(0, 0, (GLsizei) window_w, (GLsizei) window_h);
	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
	glBindTexture(GL_TEXTURE_2D, 0);
	
	//update window
	yrWindow_swapbuffers(window);
	ovrCompositor_PostPresentHandoff();

	return 0;
}

static int yrDesktop_shutdown(void)
{
	ui_ready = 0;
	yrUi_shutdown();
	if(window) {
		size_t wdata_len = 0;
		void* wdata = NULL;
		yrWindow_get_placement(window, &wdata_len, &wdata);
		yrLocalSettings_window_set(wdata_len, wdata);
		free(wdata);
		yrWindow_destroy(window);
	}
	window = NULL;
	yrLocalise_cleanup();
	yrLocalSettings_cleanup();
	return 0;
}

static int yrDesktop_freeze(void)			{ /*frozen = 1;*/ return 0; }
static int yrDesktop_unfreeze(void)			{ /*frozen = 0;*/ return 0; }

yrWindow yrDesktop_get_window(void) { return window; }
void yrDesktop_get_offset(int* x, int* y) { *x = (int)(window_w - ui_w)/2; *y = 0; }