#include "renderer.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_blendmode.h>
#include <SDL2/SDL_error.h>
#include <SDL2/SDL_rect.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_timer.h>
#include <bits/pthreadtypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <math.h>
#include "input.h"

typedef struct RenderItem {
	RenderObject_t object;
	SDL_Texture * cache;
	SDL_Rect rect;
	RenderId_t id;
} RenderItem_t;

RenderItem_t * objects = NULL;
int objectCount = 0;
int objectStorageSize = 0;
MonitorSize_t monitorSize;
pthread_t thread;
pthread_mutex_t mutex;
SDL_Window* win;


bool running;
SDL_Renderer * renderer;


static int compartSort(const RenderItem_t * a, const RenderItem_t * b) {
	return a->object.z - b->object.z;
}

//int (*compar)(const void [.size], const void [.size], void *)
void renderer_main(void(*game_main)(void)) {
	// returns zero on success else non-zero
	if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
		printf("error initializing SDL: %s\n", SDL_GetError());
	}
	win = SDL_CreateWindow("GAME", // creates a window
									SDL_WINDOWPOS_CENTERED,
									SDL_WINDOWPOS_CENTERED,
									1280, 720, 0);

	Uint32 render_flags = SDL_RENDERER_ACCELERATED;

	// creates a renderer to render our images
	renderer = SDL_CreateRenderer(win, -1, render_flags);

	if(renderer == NULL) {
		fprintf(stderr, "Could not create renderer\n");
	}

	SDL_GetRendererOutputSize(renderer, &monitorSize.width, &monitorSize.height);
	objectStorageSize = 1000;
	objects = malloc(objectStorageSize);

	input_init();

	running = true;

	while(running) {
		SDL_RenderClear(renderer);

		game_main();

		SDL_Event event;

		// Events management
		while (SDL_PollEvent(&event)) {
			switch (event.type) {

			case SDL_QUIT:
				// handling of close button
				running = false;
				printf("Quitting my job!\n");
				break;

			case SDL_KEYDOWN:
				// keyboard API for key pressed
				input_poll(event.key.keysym.scancode);
				break;
			}
		}

		//no need to make an extra render pass
		if(!running) break;


		for(int i = 0; i < objectCount; i++) {
			RenderItem_t * object = &objects[i];
			int error = SDL_RenderCopyEx(renderer, object->cache, NULL, &object->rect, object->object.angle_degre, NULL, SDL_FLIP_NONE);
			if(error != 0) {
				fprintf(stderr, "Render error in object %d with error %s\n", object->id, SDL_GetError());
				exit(1);
			}
		}
		SDL_RenderPresent(renderer);
		SDL_Delay(1000 / 400); //400fps or u a pussy
	}
}

static void buildCache(RenderItem_t * object) {
	SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, object->object.surface);
	if(texture == NULL) {
		fprintf(stderr, "Error while creating texture %s is_null: %d\n", SDL_GetError(), object->object.surface == NULL);
		exit(1);
	}
	SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
	object->cache = texture;
}

RenderId_t renderer_renderObject(RenderObject_t object) {
	RenderId_t itemId = objectCount++;

	SDL_Rect rect = {
		round(object.x * monitorSize.width),
		round(object.y * monitorSize.height),
		round(object.width * monitorSize.width),
		round(object.height * monitorSize.height)
	};

	RenderItem_t item =  {
		object,
		NULL,
		rect,
		itemId
	};

	buildCache(&item);

	objects[itemId] = item;
	//the objects are sorted
	qsort(objects, objectCount, sizeof(RenderItem_t), (__compar_fn_t)compartSort);
	return itemId;
}

void renderer_removeObject(RenderId_t id) {
	bool found = false;
	for(int i = 0; i < objectCount - 1; i++) {
		if(objects[i].id == id) {
			SDL_FreeSurface(objects[i].object.surface);
			if(objects[i].cache != NULL) {
				SDL_DestroyTexture(objects[i].cache);
			}
		}
		if(found) {
			objects[i] = objects[i + 1];
		}
	}
	objectCount--;
}

MonitorSize_t renderer_getMonitorSize() {
	return monitorSize;
}
