/* Copyright (c) 2019-2024, Sascha Willems
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 the "License";
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

 /*
  * Texture loading (and display) example (including mip maps)
  */

#pragma once

#include <ktx.h>

#include "api_vulkan_sample.h"
#include <thread>
#include <mutex>

  // Vertex layout for this example
struct TextureLoadingVertexStructure
{
	float pos[3];
	float uv[2];
	float normal[3];
};

struct PointVertex {
	float x, y, z; // 位置
	float r, g, b; // 颜色 (可以根据需要修改)
};

class TextureLoading : public ApiVulkanSample
{
public:
	struct Texture
	{
		VkSampler      sampler;
		VkImage        image;
		VkImageLayout  image_layout;
		VkDeviceMemory device_memory;
		VkImageView    view;
		uint32_t       width, height;
		uint32_t       mip_levels;
	};

	Texture texture = { 0 };
	Texture texture_point = { 0 };
	Texture texture_point_line = { 0 };
	Texture cam_text = { 0 };

	std::unique_ptr<vkb::core::BufferC> vertex_buffer;
	std::unique_ptr<vkb::core::BufferC> index_buffer;
	uint32_t                            index_count;

	std::unique_ptr<vkb::core::BufferC> vertex_buffer_point;
	uint32_t                            point_count = 0;

	std::unique_ptr<vkb::core::BufferC> vertex_buffer_point_line;
	uint32_t                            point_count_line = 0;

	struct
	{
		glm::mat4 projection;
		glm::mat4 model;
		glm::vec4 view_pos;
		float     lod_bias = 0.0f;
	} ubo_vs;

	struct
	{
		glm::mat4 projection;
		glm::mat4 model;
		glm::mat4 view;
	} ubo_vs_point;

	std::unique_ptr<vkb::core::BufferC> uniform_buffer_vs;
	std::unique_ptr<vkb::core::BufferC> uniform_buffer_vs_point;


	//glm::vec3 camear_pos_point = {0, 0, -2.f};
	//glm::vec3 face_pos_point;

	struct
	{
		VkPipeline solid;
		VkPipeline background;
		VkPipeline point;
		VkPipeline line;
	} pipelines;


	VkPipelineLayout      pipeline_layout;
	VkPipelineLayout      pipeline_layout_bg;
	VkPipelineLayout      pipeline_layout_point;
	VkPipelineLayout      pipeline_layout_point_line;

	VkDescriptorSet       descriptor_set;
	VkDescriptorSet       descriptor_set_bg;
	VkDescriptorSet       descriptor_set_point;
	VkDescriptorSet       descriptor_set_point_line;

	VkDescriptorSetLayout descriptor_set_layout;
	VkDescriptorSetLayout descriptor_set_layout_bg;
	VkDescriptorSetLayout descriptor_set_layout_point;
	VkDescriptorSetLayout descriptor_set_layout_point_line;

	static TextureLoading* Get() {
		return this_instance;
	}

	static TextureLoading* this_instance;

	TextureLoading();
	~TextureLoading();
	virtual void request_gpu_features(vkb::PhysicalDevice& gpu) override;
	void         load_texture();
	void         destroy_texture(Texture texture);
	void         build_command_buffers() override;

	void         draw();
	void		 draw_point_cloud(VkCommandBuffer command_buffer);
	void		 draw_point_cloud_line(VkCommandBuffer command_buffer);
	void         generate_quad();

	void         setup_descriptor_pool();

	void         setup_descriptor_set_layout();
	void         setup_descriptor_set();

	void         setup_descriptor_set_layout_bg();
	void         setup_descriptor_set_bg();

	void         setup_descriptor_set_layout_point();
	void		 setup_descriptor_set_point();

	void         setup_descriptor_set_layout_point_line();
	void		 setup_descriptor_set_point_line();

	void         prepare_uniform_buffers();
	void         update_uniform_buffers();

	void         prepare_uniform_buffers_point();
	void         update_uniform_buffers_point(float fov, float x, float y, float z, float sx, float sy, float sz, float rotx, float roty, float rotz, float camx, float camy, float camz);


	void         prepare_pipelines();
	void		 prepare_pipeline_bg();
	void		 prepare_pipeline_point();
	void		 prepare_pipeline_point_line();


	bool         prepare(const vkb::ApplicationOptions& options) override;
	virtual void render(float delta_time) override;
	virtual void view_changed() override;
	virtual void on_update_ui_overlay(vkb::Drawer& drawer) override;

	void            processWithVulkan(uint8_t* data, int width, int height, int rowStride, size_t dataSize, Texture& out_texture);
	void            createTexture(VkDevice device, VkPhysicalDevice physicalDevice, int width, int height, Texture& texture);
	void            updateTexture(VkDevice device, VkPhysicalDevice physicalDevice, VkCommandPool commandPool, VkQueue queue, uint8_t* data, int width, int height, int rowStride, size_t dataSize, Texture& texture);
	uint32_t        findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags properties);
	VkCommandBuffer beginSingleTimeCommands(VkDevice device, VkCommandPool commandPool);
	void            endSingleTimeCommands(VkDevice device, VkCommandPool commandPool, VkQueue queue, VkCommandBuffer commandBuffer);
	void            transitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout);

public:

	void update_point_vertex_buffer(float* pos, int pointCount);
	void update_point_vertex_buffer_line(float* pos, int pointCount_line, float r, float g, float b);
private:
	std::thread workerThread;
	bool running = false;

public:
	//void run();
	//void start();
	std::mutex mtx;
	std::mutex mtx_point;
	std::mutex mtx_point_line;
	std::mutex mtx_mvp;
	//void stop();
	//void updateTexture();

	void demo1(float* pos);
};

std::unique_ptr<vkb::Application> create_texture_loading();
