#pragma once


#if defined(_WIN32)
#include <io.h>
#include <direct.h>
#else
#include<sys/io.h> 
#include<dirent.h>
#endif



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <vector>
#include <chrono>
#include <map>

#include <locale>
#include <codecvt>
#include "algorithm"
#include <iostream>
#include <sys/stat.h>
#include <filesystem>

#include <vulkan/vulkan.h>
#include "VulkanExampleBase.h"
#include "glTFModel.h"
#include <VulkanTexture.hpp>
#include "VulkanDevice.hpp"
#include "ui.hpp"
#include <VulkanUtils.hpp>

#define ENABLE_VALIDATION false



class PlumageRender : public VulkanExampleBase
{
public:
	bool wireframe = false;
	bool normalMapping = true;
	bool ToneMapping = true;
	bool pbrEnabled = true;

	struct stat
	{

	} info ;

	struct Signal
	{
		bool imageSequenceOutputComplete = false;
		bool imageSequenceToVideoComplete = false;
		
	}signal;
 

	struct Models
	{
		glTFModel::Model scene;
		glTFModel::Model skybox;
	} models;

	struct Textures {
		vks::TextureCubeMap environmentCube;
		vks::Texture2D empty;
		vks::Texture2D lutBrdf;
		vks::TextureCubeMap irradianceCube;
		vks::TextureCubeMap prefilteredCube;
	} textures;

	struct ShaderData {
		glm::vec4 lightDir;
		float exposure = 4.5f;
		float gamma = 2.2f;
		float prefilteredCubeMipLevels;
		float scaleIBLAmbient = 1.0f;
		float debugViewInputs = 0;
		float debugViewEquation = 0;
	} shaderData;
	struct ChinesesUI
	{
		const char * model = "模型";
		
		const char* environmentMap = "环境贴图";
		const char* environmentBackGround = "启用背景贴图";
		const char* debugInput = "输入";
		const char* debugPBREquation = "PBR计算参数";
		const char* animation = "动画";
		const char* pauseAnimation = "启用动画";
		const char* animationSeq = "动画序列";
// menu item
		const char* menuFile = "文件";
		const char* menuOpenNewModel = "新模型..";
		const char* menuEnvironment = "环境光照";
		const char* menuEnvironmentConfig = "设置";
		const char* menuAnimation = "动画";
		const char* menuDebugFrameRate = "fps";
		const char* menuDebugInput = "输入";
		const char* menuAnimationNoAnimation = "当前模型没有动画!";
		
		const char* menuAnimationActivation = "开关";
		const char* menuAnimationAnimationSequence = "动画序列";


	}chineseUI;
	struct UniformBufferSet {
		Buffer scene;
		Buffer skybox;
		Buffer params;
	};

	struct UBOMatrices {
		glm::mat4 projection;
		glm::mat4 model;
		glm::mat4 view;
		glm::vec3 camPos;
	} shaderDataScene, shaderDataSkybox;

	struct PushConstBlockMaterial {
		glm::vec4 baseColorFactor;
		glm::vec4 emissiveFactor;
		glm::vec4 diffuseFactor;
		glm::vec4 specularFactor;
		float workflow;
		int colorTextureSet;
		int PhysicalDescriptorTextureSet;
		int normalTextureSet;
		int occlusionTextureSet;
		int emissiveTextureSet;
		float metallicFactor;
		float roughnessFactor;
		float alphaMask;
		float alphaMaskCutoff;
	} pushConstBlockMaterial;

	struct FilePath
	{	//model path
		std::string glTFModelFilePath = getAssetPath() + "models/DamagedHelmet/DamagedHelmet.gltf";
		std::string modelVertShaderPath = getAssetPath() + "buster_drone/shaders/glsl/mesh.vert.spv";
		std::string modelFragShaderPath = getAssetPath() + "buster_drone/shaders/glsl/mesh.frag.spv";

		//ui
		std::string uiVertShaderPath = getAssetPath() + "shaders/ui.vert.spv";
		std::string uiFragShaderPath = getAssetPath() + "shaders/ui.frag.spv";

		// skybox path
		std::string skyboxModleFilePath = getAssetPath() + "models/cube.gltf";
		std::string skyboxVertShaderPath = getAssetPath() + "shaders/skybox.vert.spv";
		std::string skyboxFragShaderPath = getAssetPath() + "shaders/skybox.frag.spv";

		std::string iblTexturesFilePath = getAssetPath() + "textures/hdr/pisa_cube.ktx";
		//tonemapping
		std::string tonemappingVertShaderPath = getAssetPath() + "buster_drone/shaders/glsl/genbrdflut.vert.spv";
		std::string tonemappingEnableFragShaderPath = getAssetPath() + "buster_drone/shaders/glsl/tonemapping_enable.frag.spv";
		std::string tonemappingDisableFragShaderPath = getAssetPath() + "buster_drone/shaders/glsl/tonemapping_disable.frag.spv";


		//  cube map
		std::string irradianceFragShaderPath = getAssetPath() + "shaders/irradiancecube.frag.spv";

		std::string filterVertShaderPath = getAssetPath() + "shaders/filtercube.vert.spv";

		std::string prefilterEnvmapFragShaderPath = getAssetPath() + "shaders/prefilterenvmap.frag.spv";
		//brdf cube map
		std::string brdfVertShaderPath = getAssetPath() + "shaders/genbrdflut.vert.spv";
		std::string brdfFragShaderPath = getAssetPath() + "shaders/genbrdflut.frag.spv";
		// environment map texture
		std::string envMapFilePath = getAssetPath() + "environments/papermill.ktx";
		std::string emptyEnvmapFilePath = getAssetPath() + "textures/empty.ktx";
		// pbr shader
		std::string pbrVertShaderPath = getAssetPath() + "shaders/pbr.vert.spv";
		std::string pbrFragShaderPath = getAssetPath() + "shaders/pbr_khr.frag.spv";

		//ttf file path
		std::string ttfFilePath = getAssetPath() + "/data/Roboto-Medium.ttf";

		// output file path

		std::string imageOutputPath = getAssetPath() + "output/imageSequence";
		std::string videoOutputPath = getAssetPath() + "output/video";
		std::string totalImageOutputPath;

		// script file path
		std::string image2videoBatFilePath = getAssetPath() + "script/image2video.bat";
		std::string image2videoShFilePath = getAssetPath() + "script/image2video.sh";

	} filePath;

	
	glm::vec3 modelrot = glm::vec3(0.0f);
	glm::vec3 modelPos = glm::vec3(0.0f);
	

	enum PBRWorkflows { PBR_WORKFLOW_METALLIC_ROUGHNESS = 0, PBR_WORKFLOW_SPECULAR_GLOSINESS = 1 };


	std::map<std::string, std::string> environments;
	std::string selectedEnvironment = "papermill";
	std::map<std::string, std::string> scenes;
	std::string selectedScene = "DamagedHelmet";

	int32_t debugViewInputs = 0;
	int32_t debugViewEquation = 0;

	struct StagingBuffer {
		VkBuffer buffer;
		VkDeviceMemory memory;
	} vertexStaging, indexStaging;

	struct Pipelines {
		VkPipeline skybox;
		VkPipeline pbr;
		VkPipeline pbrDoubleSided;
		VkPipeline pbrAlphaBlend;
		VkPipeline solid;
		VkPipeline wireframe = VK_NULL_HANDLE;
		VkPipeline toneMapping = VK_NULL_HANDLE;
	} pipelines;

	VkPipeline boundPipeline = VK_NULL_HANDLE;

	struct  PipelineLayouts
	{
		VkDescriptorSetLayout scene;
		VkDescriptorSetLayout material;
		VkDescriptorSetLayout node;
		VkPipelineLayout tonemappingLayout;
	} pipelineLayouts;

	VkPipelineLayout pipelineLayout;

	struct DescriptorSets {
		VkDescriptorSet scene;
		VkDescriptorSet skybox;
		VkDescriptorSet tonemappingDescriptorSet = VK_NULL_HANDLE;
	};
	
	

	struct DescriptorSetLayouts {
		VkDescriptorSetLayout scene;
		VkDescriptorSetLayout material;
		VkDescriptorSetLayout node;
	} descriptorSetLayouts;

	std::vector<DescriptorSets> descriptorSets;

	std::vector<VkCommandBuffer> commandBuffers;
	std::vector<UniformBufferSet> uniformBuffers;

	std::vector<VkFence> waitFences;
	std::vector<VkSemaphore> renderCompleteSemaphores;
	std::vector<VkSemaphore> presentCompleteSemaphores;

	const uint32_t renderAhead = 2;
	uint32_t frameIndex = 0;

	//VkImage swapChainImage;

	int32_t animationIndex = 0;
	float animationTimer = 0.0f;
	bool animate = true;

	bool displayBackground = true;

	struct LightSource {
		glm::vec3 color = glm::vec3(1.0f);
		glm::vec3 rotation = glm::vec3(75.0f, 40.0f, 0.0f);
	} lightSource;


	//cube map generation
	
	struct OffScreen
	{
		VkImage image;
		VkImageView view;
		VkDeviceMemory memory;
		VkFramebuffer framebuffer;
	} offscreen;
	
	struct IrradiancePushBlock
	{
		glm::mat4 mvp;
		// Sampling deltas
		float deltaPhi = (2.0f * float(M_PI)) / 180.0f;
		float deltaTheta = (0.5f * float(M_PI)) / 64.0f;
	} irradiancePushBlock;

	struct PrefilterPushBlock {
		glm::mat4 mvp;
		float roughness;
		uint32_t numSamples = 32u;
	} prefilterPushBlock;

	UI* gui;

	uint64_t savedFrameCounter = settings.startFrameCount;
	

	PlumageRender();
	~PlumageRender()
	{
		// Clean up used Vulkan resources
		// Note : Inherited destructor cleans up resources stored in base class
		vkDestroyPipeline(device, pipelines.skybox, nullptr);
		vkDestroyPipeline(device, pipelines.pbr, nullptr);
		vkDestroyPipeline(device, pipelines.pbrAlphaBlend, nullptr);

		vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
		vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.scene, nullptr);
		vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.material, nullptr);
		vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.node, nullptr);

		models.scene.destroy(device);
		models.skybox.destroy(device);

		for (auto buffer : uniformBuffers) {
			buffer.params.destroy();
			buffer.scene.destroy();
			buffer.skybox.destroy();
		}
		for (auto fence : waitFences) {
			vkDestroyFence(device, fence, nullptr);
		}
		for (auto semaphore : renderCompleteSemaphores) {
			vkDestroySemaphore(device, semaphore, nullptr);
		}
		for (auto semaphore : presentCompleteSemaphores) {
			vkDestroySemaphore(device, semaphore, nullptr);
		}

		textures.environmentCube.destroy();
		textures.irradianceCube.destroy();
		textures.prefilteredCube.destroy();
		textures.lutBrdf.destroy();
		textures.empty.destroy();
		delete gui;
	}

	
	void renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFModel::Material::AlphaMode alphaMode);
	void loadScene(std::string filename);
	void loadEnvironment(std::string filename);
	void         buildCommandBuffers();
	void         loadAssets();
	void setupNodeDescriptorSet(glTFModel::Node* node);
	void setupDescriptors();
	void         preparePipelines();
	// void tonemappingPipelin();
	void generateCubemaps();
	void generateBRDFLUT();
	void         prepareUniformBuffers();
	void         updateUniformBuffers();
	void updateShaderData();
	void windowResized();
	void         prepare();
	void submitWork(VkCommandBuffer cmdBuffer, VkQueue queue);

	void writeImageToFile(std::string filePath);
	void outputImageSequence();
	void imageSequenceToVideo();
	//void outputScreenShot();
	uint32_t getMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties);
	virtual void render();
	virtual void updateUIOverlay();
	virtual void fileDropped(std::string filename);
};