#include "renderer.h"
#include "scene.h"
#include <SDL2/SDL_blendmode.h>
#include <SDL2/SDL_error.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_surface.h>
#include <stdio.h>
#include <string.h>

const RenderObject_t EMPTY_RENDER_OBJECT = { 0, 0.f, 0.f, 0.f, 0.f, 0.f, NULL, "Undefined" };

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

void scene_render(const Scene_t * scene) {
	MonitorSize_t monitorSize = renderer_getMonitorSize();

	for(int i = 0; i < scene->objectCount; i++) {
		const SceneObject_t * object = &scene->objects[i];
		float x = (object->x - scene->x) / CAMERA_WIDTH;
		float y = (object->y - scene->y) / CAMERA_HEIGHT;
		//the object is to be rendered

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

		int error;

		if(object->renderObj.angle_x == FLT_MIN || object->renderObj.angle_y == FLT_MIN){
			error = SDL_RenderCopyEx(renderer, object->cache, NULL, &rect, object->renderObj.angle_degre, NULL, SDL_FLIP_NONE);
		}
		else{
			SDL_Point center;
			center.x = object->renderObj.angle_x * rect.w;
			center.x = object->renderObj.angle_y * rect.h;
			error = SDL_RenderCopyEx(renderer, object->cache, NULL, &rect, object->renderObj.angle_degre, &center, SDL_FLIP_NONE);
		}
		if(error != 0) {
			fprintf(stderr, "Render error in object %d with error %s\n", object->id, SDL_GetError());
			exit(1);
		}
	}
}

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

RenderId_t scene_addRenderObject(Scene_t * scene, RenderObject_t object, int x, int y) {
	scene->objectCount++;
	RenderId_t itemId = scene->lastId++;

	if(scene->objectCount * sizeof(SceneObject_t) > scene->objectArraySize) {
		scene->objectArraySize = scene->objectCount * sizeof(SceneObject_t);
		scene->objects = realloc(scene->objects, scene->objectArraySize);
		if(scene->objects == NULL) {
			printf("Realloc failed\n");
		}
	}

	SceneObject_t item = {
		x, y,
		object,
		NULL,
		itemId
	};

	buildCache(&item);

	scene->objects[scene->objectCount - 1] = item;

	//the objects are sorted
	qsort(scene->objects, scene->objectCount, sizeof(SceneObject_t), (__compar_fn_t)compartSort);
	return itemId;
}

static int indexof(Scene_t * scene, RenderId_t id) {
	bool found = false;
	int index = 0;
	for(int i = 0; i < scene->objectCount; i++) {
		if(scene->objects[i].id == id){
			found = true;
			index = i;
			break;
		}
	}
	if(!found){
		printf("object %d not found\n", id);
		return -1;
	}

	return index;
}

void scene_moveRenderObject(Scene_t * scene, RenderId_t id, float x, float y) {
	int index = indexof(scene, id);
	if(index < 0)
		return;

	SceneObject_t * object = &scene->objects[index];
	object->x = x;
	object->y = y;
}

void scene_removeRenderObject(Scene_t * scene, RenderId_t id) {
	int index = indexof(scene, id);
	if(index < 0)
		return;

	scene->objectCount--;

	for(int i = index; i < scene->objectCount; i++) {
		scene->objects[i] = scene->objects[i + 1];
	}
}

void scene_free(Scene_t * scene) {
	for(int i = 0; i < scene->objectCount - 1; i++) {
		SDL_FreeSurface(scene->objects[i].renderObj.surface);
		if(scene->objects[i].cache != NULL) {
			SDL_DestroyTexture(scene->objects[i].cache);
		}
	}
	free(scene->objects);
	scene->objectArraySize = 0;
	scene->objectCount = 0;
	scene->objects = NULL;
}
