#pragma once

#include <vulkan/vulkan.h>
#include "SceneObject.h"


/**
 * Represents a house object within a Vulkan-rendered scene.
 * Inherits from SceneObject to leverage common object properties and functionalities.
 */
class TreeObject : public SceneObjectWithPhysics{
    public:
    
     /**
     * Constructor for the HouseObject.
     * Initializes the house geometry and invokes the base SceneObject constructor.
     * Scalability
     * 
     * @param dev The Vulkan device.
     * @param phyDev The physical Vulkan device.
     * @param descPool The descriptor pool for allocating descriptor sets.
     * @param descSetLayout The layout of the descriptor set.
     * @param queue The Vulkan queue to which rendering commands will be submitted.
     */
        TreeObject(
            VkDevice dev, 
            VkPhysicalDevice phyDev, 
            VkDescriptorPool descPool, 
            VkDescriptorSetLayout descSetLayout, 
            VkQueue queue, 
            Physics::GamePhysicsManager& inputPhysicsManager, 
            const glm::vec3& initialCollisionBoxPosition, 
            const glm::vec3& initialCollisionBoxHalfSidelengths,
            VkSampler sampler,
            VkSampler normalSampler,
            VkCommandPool cmdPool,
            bool sync,
            PFN_vkCmdPipelineBarrier2KHR pfn,
            glm::mat4 transformation,
            ShadowMap& shadowMap,
            int treeIndex,
            const float& inputMass = 0.f)
        : SceneObjectWithPhysics(
        dev, 
        phyDev, 
        descPool, 
        descSetLayout, 
        queue, 
        {
            {{-1.0f, -1.0f, -1.0f}}, // Bottom-left back corner
            {{1.0f, -1.0f, -1.0f}},  // Bottom-right back corner
            {{1.0f, -1.0f, 1.0f}},   // Bottom-right front corner
            {{-1.0f, -1.0f, 1.0f}},  // Bottom-left front corner
            {{-1.0f, 1.0f, -1.0f}}, // Top-left back corner
            {{1.0f, 1.0f, -1.0f}},  // Top-right back corner
            {{1.0f, 1.0f, 1.0f}},   // Top-right front corner
            {{-1.0f, 1.0f, 1.0f}},  // Top-left front corner
        }, 
        {
            // Cube indices
            0, 1, 2, 2, 3, 0, // Front face
            1, 5, 6, 6, 2, 1, // Right face
            5, 4, 7, 7, 6, 5, // Back face
            4, 0, 3, 3, 7, 4, // Left face
            4, 5, 1, 1, 0, 4, // Bottom face
            3, 2, 6, 6, 7, 3  // Top face
        },
        inputPhysicsManager, 
        initialCollisionBoxPosition, 
        initialCollisionBoxHalfSidelengths,
        shadowMap,
        inputMass
        ){
        
        // glm::mat4 rotationMatrix = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f));
        // glm::mat4 scalingMatrix = glm::scale(glm::mat4(1.0f), glm::vec3(1.0f, 1.0f, 1.0f));
        // glm::mat4 translationMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f));
        uniformBuffer = vklCreateHostCoherentBufferWithBackingMemory(
            sizeof(UniformBuffer), VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
        );

        std::string objFilePath = getTreeModelPath(treeIndex);
        if (!load_from_obj(objFilePath.c_str())) {
            throw std::runtime_error("Failed to load tree model from " + objFilePath);
        }
        applyTransformationsLocal(this->data.positions, transformation);
        this->sceneObjectGeometry = createAndUploadIntoGpuMemory(this->data);

        for (size_t i = 0; i < materials.size(); ++i) {
            const auto& material = materials[i];

            ubo.ambientConstant = material.ambient[0];
            ubo.diffuseConstant = material.diffuse[0];
            ubo.specularConstant = material.specular[0];
            ubo.shininessConstant = material.shininess;

            if (!material.diffuse_texname.empty()) {
                std::string texture_path = getTreeTexturePath(treeIndex);
                ImageAndView texture = loadImage(dev, queue, cmdPool, texture_path, sync, pfn);

                VkDescriptorSet descriptorSet = allocDescriptorSet(dev, descPool, descriptorSetLayout);

                ImageAndView normalImageAndView = loadImage(device, queue, cmdPool, getTreeTexturePath(treeIndex), sync, pfn);
                writeDescriptorSet(device, descriptorSet, uniformBuffer, texture.view, sampler, normalImageAndView.view, normalSampler);

                // Write shadowmap to correct binding in the descriptorset (default binding = 4)
                writeDescriptorSet(device, descriptorSet, shadowMap.m_image_view, shadowMap.m_image_sampler, shadowMap.m_descriptorBinding);

                materialDescriptorSets[i] = descriptorSet;
            }
        }
       
        ubo.color = glm::vec4(0.4f, 0.26f, 0.13f, 1.0f); //brown
        setObjectState(ObjectState::PhysicsControlled);

    }

    /**
     * Records the Vulkan rendering commands for the house object.
     * Binds the necessary Vulkan resources and issues the draw call.
     * 
     * @param commandBuffer The command buffer to record the rendering commands.
     * @param pipeline_layout The pipeline layout used for descriptor binding.
     * @param pipeline The graphics pipeline to use for rendering the house.
     */
    void recordRenderingCommands(VkCommandBuffer commandBuffer, VkPipeline hudPipeline, VkPipeline specularPipeline, VkPipeline normalPipeline, VkPipeline normalheightPipeline, const FrameTimer& frameTimer){        
        drawObject(normalPipeline);
    }

private:
    /**
     * Returns the file path of the tree model based on the index.
     * 
     * @param index The index to determine which tree model to load.
     * @return The file path of the tree model.
     */
    std::string getTreeModelPath(int index) {
        switch (index) {
            case 0: return "assets/meshes/tree10.obj";
            case 1: return "assets/meshes/tree4.obj";
            case 2: return "assets/meshes/tree6.obj";
            case 3: return "assets/meshes/tree5.obj";
            // Add more cases for different tree models
            default: return "assets/meshes/tree10.obj";
        }
    }

    /**
     * Returns the file path of the tree texture based on the index.
     * 
     * @param index The index to determine which tree texture to load.
     * @return The file path of the tree texture.
     */
    std::string getTreeTexturePath(int index) {
        switch (index) {
            case 0: return "assets/textures/10_tree.dds";
            case 1: return "assets/textures/4_tree.dds";
            case 2: return "assets/textures/6_tree.dds";
            case 3: return "assets/textures/5_tree.dds";
            // Add more cases for different tree textures
            default: return "assets/textures/tree10_diffuse.dds";
        }
    }

    


};

