#include "DeerRender/VoxelWorld.h"

#include "Deer/Voxels/Chunk.h"
#include "Deer/Voxels/VoxelWorldData.h"
#include "DeerRender/Application.h"
#include "DeerRender/Components.h"
#include "DeerRender/Log.h"
#include "DeerRender/Tools/Memory.h"
#include "DeerRender/Voxel.h"

#include "DeerRender/Render/Render.h"
#include "DeerRender/Render/RenderUtils.h"
#include "DeerRender/Render/Texture.h"
#include "DeerRender/Scene.h"
#include "DeerRender/Voxels/VoxelWorldRenderData.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_inverse.hpp"
#include "glm/gtc/matrix_transform.hpp"

namespace Deer {
	VoxelLight VoxelWorld::readLight(VoxelCordinates coords) {
		DEER_CORE_ASSERT(initialized, "Voxel World is not initialized");

		ChunkID chunkID;
		ChunkVoxelID chunkVoxelID;

		extractChunkCordinates(coords, chunkID, chunkVoxelID);
		if (!worldProps.isValid(chunkID))
			return lightVoxel;

		Chunk& chunk = chunks[worldProps.getWorldChunkID(chunkID)];
		return chunk.readLight(chunkVoxelID);
	}

	VoxelLight& VoxelWorld::modLight(VoxelCordinates coords) {
		DEER_CORE_ASSERT(initialized, "Voxel World is not initialized");

		ChunkID chunkID;
		ChunkVoxelID chunkVoxelID;

		extractChunkCordinates(coords, chunkID, chunkVoxelID);
		if (!worldProps.isValid(chunkID))
			return lightVoxel;

		chunkQueue.addChunk(chunkID);

		Chunk& chunk = chunks[worldProps.getWorldChunkID(chunkID)];
		return chunk.modLight(chunkVoxelID);
	}

	void VoxelWorld::render(const SceneCamera& camera) {
		DEER_CORE_ASSERT(initialized, "Voxel World is not initialized");

		glm::mat4 camMatrix = glm::inverse(camera.transform.getMatrix());
		glm::mat4 projectionMatrix = camera.camera.getMatrix();
		glm::mat4 invertZ = glm::scale(glm::mat4(1.0f), glm::vec3(1, 1, -1));

		// Lets invert the z axis for engine convenience
		glm::mat4 cameraProjectionMatrix =
		    projectionMatrix * invertZ * camMatrix;

		// DataStore::getVoxelColorTextureAtlas()->bind(0);

		// Ref<Shader>& shader = DataStore::getSolidVoxelShader();
		// shader->bind();

		// shader->uploadUniformMat4("u_viewMatrix", cameraProjectionMatrix);
		// shader->uploadUniformMat4("u_worldMatrix", glm::mat4(1.0f));
		// shader->uploadUniformInt("u_texture", 0);
		// shader->uploadUniformInt("u_textureSize",
		//                          DataStore::getVoxelTextureAtlasSize());

		for (int x = 0; x < worldProps.getChunkCount(); x++) {
			ChunkRender& chunkRender = chunksRender[x];
			if (!chunkRender.hasData)
				continue;

			ChunkID chunkID = worldProps.getChunkID(x);
			chunkRender.solidVoxel->bind();

			// shader->uploadUniformInt("u_chunkID_x", chunkID.x);
			// shader->uploadUniformInt("u_chunkID_y", chunkID.y);
			// shader->uploadUniformInt("u_chunkID_z", chunkID.z);

			Render::submit(*chunkRender.solidVoxel);
		}
	}
} // namespace Deer