#include <render.h>

RenderCtx r_ctx;

// Possible window flags
/*
FLAG_VSYNC_HINT
FLAG_FULLSCREEN_MODE    -> not working properly -> wrong scaling!
FLAG_WINDOW_RESIZABLE
FLAG_WINDOW_UNDECORATED
FLAG_WINDOW_TRANSPARENT
FLAG_WINDOW_HIDDEN
FLAG_WINDOW_MINIMIZED   -> Not supported on window creation
FLAG_WINDOW_MAXIMIZED   -> Not supported on window creation
FLAG_WINDOW_UNFOCUSED
FLAG_WINDOW_TOPMOST
FLAG_WINDOW_HIGHDPI     -> errors after minimize-resize, fb size is recalculated
FLAG_WINDOW_ALWAYS_RUN
FLAG_MSAA_4X_HINT
*/

void	LoadPipeline() {
	r_ctx.width = 800;
	r_ctx.height = 480;

	r_ctx.camera3d.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position
    r_ctx.camera3d.target = (Vector3){ 0.0f, 0.0f, 0.0f };      // Camera looking at point
    r_ctx.camera3d.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
    r_ctx.camera3d.fovy = 45.0f;                                // Camera field-of-view Y
    r_ctx.camera3d.projection = CAMERA_PERSPECTIVE;             // Camera projection type

	r_ctx.hud_on = true;
	r_ctx.debug_on = true;
	InitWindow(r_ctx.width, r_ctx.height, "GAME!!");
	assert(IsWindowReady());
	r_ctx.window = GetWindowHandle();
	
	r_ctx.shader.deferred = LoadShader("source/shader/deferred.vs", "source/shader/deferred.fs");
	r_ctx.shader.gbuffer = LoadShader("source/shader/gbuffer.vs", "source/shader/gbuffer.fs");
	r_ctx.shader.filter = LoadShader("", "source/shader/Screen_filter.fs");

	r_ctx.gbuffer.framebufferId = rlLoadFramebuffer();
	assert(r_ctx.gbuffer.framebufferId);

	rlEnableFramebuffer(r_ctx.gbuffer.framebufferId);
	r_ctx.gbuffer.depth_tex_id = rlLoadTextureDepth(r_ctx.width, r_ctx.height, false);
	// If Artefact appear on Normal, may need to change to RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32 and also need to be used in case of anti aliasing (MLAA or SMAA if needed)
	r_ctx.gbuffer.normal_tex_id = rlLoadTexture(NULL, r_ctx.width, r_ctx.height, RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16, 1);
    // The color in RGB, and the specular strength in the alpha channel
	r_ctx.gbuffer.albedoSpec_tex_id = rlLoadTexture(NULL, r_ctx.width, r_ctx.height, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
	rlActiveDrawBuffers(3);

	rlFramebufferAttach(r_ctx.gbuffer.framebufferId, r_ctx.gbuffer.depth_tex_id, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_TEXTURE2D, 0);
	rlFramebufferAttach(r_ctx.gbuffer.framebufferId, r_ctx.gbuffer.normal_tex_id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0);
	rlFramebufferAttach(r_ctx.gbuffer.framebufferId, r_ctx.gbuffer.albedoSpec_tex_id, RL_ATTACHMENT_COLOR_CHANNEL1, RL_ATTACHMENT_TEXTURE2D, 0);
	assert(rlFramebufferComplete(r_ctx.gbuffer.framebufferId));

	rlEnableShader(r_ctx.shader.deferred.id);
    int texUnitDepth = 0;
    int texUnitNormal = 1;
    int texUnitAlbedoSpec = 2;
    SetShaderValue(r_ctx.shader.deferred, rlGetLocationUniform(r_ctx.shader.deferred.id, "gDepth"), &texUnitDepth, RL_SHADER_UNIFORM_SAMPLER2D);
    SetShaderValue(r_ctx.shader.deferred, rlGetLocationUniform(r_ctx.shader.deferred.id, "gNormal"), &texUnitNormal, RL_SHADER_UNIFORM_SAMPLER2D);
    SetShaderValue(r_ctx.shader.deferred, rlGetLocationUniform(r_ctx.shader.deferred.id, "gAlbedoSpec"), &texUnitAlbedoSpec, RL_SHADER_UNIFORM_SAMPLER2D);
    rlDisableShader();
	//now need to set this to things that use this Material, and add light used in the scene.

	rlEnableDepthTest();
	rlEnableBackfaceCulling();
	TraceLog(LOG_INFO, "Render Pipeline Built");
	SetTargetFPS(120);//may change
	DisableCursor();
}

void	UnloadPipeline() {
	rlUnloadFramebuffer(r_ctx.gbuffer.framebufferId);
	rlUnloadTexture(r_ctx.gbuffer.depth_tex_id);
	rlUnloadTexture(r_ctx.gbuffer.normal_tex_id);
	rlUnloadTexture(r_ctx.gbuffer.albedoSpec_tex_id);
	UnloadShader(r_ctx.shader.deferred);
	UnloadShader(r_ctx.shader.gbuffer);
	UnloadShader(r_ctx.shader.filter);
	CloseWindow();
}

void	LoadCameraMode(int mode) {
	switch (mode) {
		case(0): {
			break;
		}
		default:
			break;
	}
}

//need to work on a way to structurize my data for the scene to be rendered
void	RenderingDeferred3D(const Scene* scene, const ModelRegistry *models, const AnimationRegistry *animations, const TextureRegistry *textures, const FontRegistry *fonts) {
	(void)scene;
	(void)models;
	(void)animations;
	(void)textures;
	UpdateCamera(&r_ctx.camera3d, CAMERA_FREE);
	if (IsKeyPressed(KEY_Z)) r_ctx.camera3d.target = (Vector3){ 0.0f, 0.0f, 0.0f };
	BeginDrawing();
	ClearBackground(BLACK);
	rlEnableDepthMask();
	rlEnableFramebuffer(r_ctx.gbuffer.framebufferId);
	rlEnableShader(r_ctx.shader.gbuffer.id);
	BeginMode3D(r_ctx.camera3d);
	//MainGeamoetry;
	//for (int i = 0; i < scene->count; i++) {
    //    RenderEntity *e = &scene->entities[i];
    //    Model m = models->models[e->modelId];

    //    switch (e->type) {
    //        case R_ENTITY_STATIC:
    //            DrawModel(m, (Vector3){0}, 1.0f, WHITE); 
    //            break;

    //        case R_ENTITY_ANIMATED:
    //            UpdateModelAnimation(m, animation->animations[e->data.anim.activeAnim], e->data.anim.time);
    //            DrawModel(m, (Vector3){0}, 1.0f, WHITE);
    //            break;

    //        case R_ENTITY_INSTANCED:
    //            // Note: Raylib's DrawMeshInstanced handles the instancing buffer
    //            for (int j = 0; j < m.meshCount; j++) {
    //                DrawMeshInstanced(m.meshes[j], m.materials[0], e->data.instancing.transforms, e->data.instancing.instanceCount);
    //            }
    //            break;
    //    }
    //}
	EndMode3D();
	rlDisableShader();
	rlDisableFramebuffer();
	rlDisableDepthMask();//may not be usefull
	//rlDisableDepthTest();

	//Light Pass and forward pass;
	//I should build a shader that take every light in the scene and make a 3D heightmap of lighting then compute it once for all static light allowing to cheat computing everylight
	//light could be computed into a scalar field
	//Light Pass
	BeginMode3D(r_ctx.camera3d);
		DrawCube(Vector3Zero(), 1, 1, 1, GREEN);
	EndMode3D();
	//Screen Filter Pass
	//BeginShaderMode(r_ctx.shader.filter);//may be good to have this disabled for accessibility
	////DrawTextureRec();
	//EndShaderMode();
	DrawFPS(10, 10);
	DrawTextEx(fonts->font[DENDRITIC_VOLTAGE] , "Hello", (Vector2){40, 40}, 16, 1, RED);
	//render ui
	EndDrawing();
}
