#pragma once

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

class RobotHeadObject : public SceneObject{
    public:
    
     /**
     * Constructor for the RobotHead. Used by the Robot Object below to have a rotating head
     * Initializes the 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.
     */
        RobotHeadObject(VkDevice dev, VkPhysicalDevice phyDev, VkDescriptorPool descPool, VkDescriptorSetLayout descSetLayout, VkQueue queue,
        VkSampler sampler,
        VkSampler normalSampler,
        VkCommandPool cmdPool,
        bool sync,
        PFN_vkCmdPipelineBarrier2KHR pfn,
        ShadowMap& shadowMap)
        : SceneObject(dev, phyDev, descPool, descSetLayout, queue, 
        {
            {{-0.2f, -0.2f, -0.2f}}, // Bottom-left back corner
            {{0.2f, -0.2f, -0.2f}},  // Bottom-right back corner
            {{0.2f, -0.2f, 0.2f}},   // Bottom-right front corner
            {{-0.2f, -0.2f, 0.2f}},  // Bottom-left front corner
            {{-0.2f, 0.2f, -0.2f}}, // Top-left back corner
            {{0.2f, 0.2f, -0.2f}},  // Top-right back corner
            {{0.2f, 0.2f, 0.2f}},   // Top-right front corner
            {{-0.2f, 0.2f, 0.2f}},  // 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
        }, sampler, cmdPool, sync, pfn, shadowMap)
    {
        // Initialization is mainly handled by the SceneObject constructor
        GeometryData data;
            data.positions = 
            {{-0.02f, -0.02f, -0.5f}, // Bottom-left back corner
            {0.02f, -0.02f, -0.5f},  // Bottom-right back corner
            {0.02f, -0.02f, 0.0f},   // Bottom-right front corner
            {-0.02f, -0.02f, 0.0f},  // Bottom-left front corner
            {-0.02f, 0.02f, -0.5f}, // Top-left back corner
            {0.02f, 0.02f, -0.5f},  // Top-right back corner
            {0.02f, 0.02f, 0.0f},   // Top-right front corner
            {-0.02f, 0.02f, 0.0f}}  // Top-left front corner
            ;
            data.indices =
              {
            // 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
                };
                 data.normals = {
                    // Normals for each face (flat shading)
                    {0.0f, -1.0f, 0.0f}, // Bottom face
                    {0.0f, -1.0f, 0.0f},
                    {0.0f, -1.0f, 0.0f},
                    {0.0f, -1.0f, 0.0f},
                    {0.0f, 1.0f, 0.0f}, // Top face
                    {0.0f, 1.0f, 0.0f},
                    {0.0f, 1.0f, 0.0f},
                    {0.0f, 1.0f, 0.0f},
                    {1.0f, 0.0f, 0.0f}, // Right face
                    {1.0f, 0.0f, 0.0f},
                    {1.0f, 0.0f, 0.0f},
                    {1.0f, 0.0f, 0.0f},
                    {-1.0f, 0.0f, 0.0f}, // Left face
                    {-1.0f, 0.0f, 0.0f},
                    {-1.0f, 0.0f, 0.0f},
                    {-1.0f, 0.0f, 0.0f},
                    {0.0f, 0.0f, 1.0f}, // Front face
                    {0.0f, 0.0f, 1.0f},
                    {0.0f, 0.0f, 1.0f},
                    {0.0f, 0.0f, 1.0f},
                    {0.0f, 0.0f, -1.0f}, // Back face
                    {0.0f, 0.0f, -1.0f},
                    {0.0f, 0.0f, -1.0f},
                    {0.0f, 0.0f, -1.0f}
                };
                data.colors = {
                {1.0f, 0.0f, 0.0f, 1.0f}, // Red for bottom-left back corner
                {0.0f, 1.0f, 0.0f, 1.0f}, // Green for bottom-right back corner
                {0.0f, 0.0f, 1.0f, 1.0f}, // Blue for bottom-right front corner
                {1.0f, 1.0f, 0.0f, 1.0f}, // Yellow for bottom-left front corner
                {1.0f, 0.0f, 1.0f, 1.0f}, // Magenta for top-left back corner
                {0.0f, 1.0f, 1.0f, 1.0f}, // Cyan for top-right back corner
                {1.0f, 1.0f, 1.0f, 1.0f}, // White for top-right front corner
                {0.0f, 0.0f, 0.0f, 1.0f}  // Black for top-left front corner
            };
        uniformBuffer = vklCreateHostCoherentBufferWithBackingMemory(
            sizeof(UniformBuffer), VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
        );

        // Ensure the object is loaded
        if (!load_from_obj("assets/meshes/head.obj")) {
            throw std::runtime_error("Failed to load cube_triangl_mod.obj");
        }

        // Create and upload geometry to GPU
        this->sceneObjectGeometry = createAndUploadIntoGpuMemory(this->data);

        ubo.ambientConstant = 0.1f; // Lower ambient reflection
        ubo.diffuseConstant = 0.2f; // Lower diffuse reflection
        ubo.specularConstant = 1.0f; // High specular reflection for metal
        ubo.shininessConstant = 32.0f;


        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 = "assets/textures/" + material.diffuse_texname;
                    ImageAndView texture = loadImage(dev, queue, cmdPool, texture_path, sync, pfn);

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

                    ImageAndView normalImageAndView = loadImage(device, queue, cmdPool, "assets/textures/RoundAluminiumPanel01_4K_Normal.dds", 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);

                    // 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, 1, 0, 1);
    }

    virtual void updateTransforms(const FrameTimer& frameTimer) override {
        glm::mat4 localTranslation = glm::translate(glm::mat4(1.0f), glm::vec3(0, 0.1, 0));
        glm::mat4 localRotation = glm::rotate(glm::mat4(1.0f), glm::sin(frameTimer.timePassedSinceStart), glm::vec3(0, 1, 0));
        localTransform = localTranslation * localRotation;

        // Update the world transform by combining with the parent's world transform
        if (parent) {
            worldTransform = parent->getWorldTransform() * localTransform;
        } else {
            worldTransform = localTransform;
        }

        // Propagate the transform update to children
        for (auto& child : children) {
            child->updateTransforms(frameTimer);
        }
    }

        /**
         * Records the Vulkan rendering commands for the robot body 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 pipeline, VkPipeline specularPipeline, VkPipeline normalPipeline, VkPipeline normalheightPipeline, const FrameTimer& frameTimer){
            

            //if (frameTimer.timePassedSinceStart > 5.0f){
                drawObject(normalPipeline);

                for (auto& child : children) {
                    child->recordRenderingCommands(commandBuffer, pipeline, specularPipeline, normalPipeline, normalheightPipeline, frameTimer);
                }
            //}
        }

};

class RobotArm : public SceneObject{
    public:
    glm::vec3 mOffset= glm::vec3(0,0,0);
    
     /**
     * Constructor for the RobotHead. Used by the Robot Object below to have a rotating head
     * Initializes the 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.
     */
        RobotArm(VkDevice dev, VkPhysicalDevice phyDev, VkDescriptorPool descPool, VkDescriptorSetLayout descSetLayout, VkQueue queue, const std::string& side,
        VkSampler sampler,
        VkSampler normalSampler,
        VkCommandPool cmdPool,
        bool sync,
        PFN_vkCmdPipelineBarrier2KHR pfn,
        ShadowMap& shadowMap)
        : SceneObject(dev, phyDev, descPool, descSetLayout, queue, 
        {
            {{-0.02f, -0.02f, -0.5f}}, // Bottom-left back corner
            {{0.02f, -0.02f, -0.5f}},  // Bottom-right back corner
            {{0.02f, -0.02f, 0.0f}},   // Bottom-right front corner
            {{-0.02f, -0.02f, 0.0f}},  // Bottom-left front corner
            {{-0.02f, 0.02f, -0.5f}}, // Top-left back corner
            {{0.02f, 0.02f, -0.5f}},  // Top-right back corner
            {{0.02f, 0.02f, 0.0f}},   // Top-right front corner
            {{-0.02f, 0.02f, 0.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
        }, 
        sampler, 
        cmdPool, 
        sync, 
        pfn,
        shadowMap)
    {
        GeometryData data;
            data.positions = 
                {{-0.02f, -0.02f, -0.5f}, // Bottom-left back corner
            {0.02f, -0.02f, -0.5f},  // Bottom-right back corner
            {0.02f, -0.02f, 0.0f},   // Bottom-right front corner
            {-0.02f, -0.02f, 0.0f},  // Bottom-left front corner
            {-0.02f, 0.02f, -0.5f}, // Top-left back corner
            {0.02f, 0.02f, -0.5f},  // Top-right back corner
            {0.02f, 0.02f, 0.0f},   // Top-right front corner
            {-0.02f, 0.02f, 0.0f}}  // Top-left front corner
                ;
            data.indices =
                {
            // 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
        };
        data.normals = {
            // Normals for each face (flat shading)
            {0.0f, -1.0f, 0.0f}, // Bottom face
            {0.0f, -1.0f, 0.0f},
            {0.0f, -1.0f, 0.0f},
            {0.0f, -1.0f, 0.0f},
            {0.0f, 1.0f, 0.0f}, // Top face
            {0.0f, 1.0f, 0.0f},
            {0.0f, 1.0f, 0.0f},
            {0.0f, 1.0f, 0.0f},
            {1.0f, 0.0f, 0.0f}, // Right face
            {1.0f, 0.0f, 0.0f},
            {1.0f, 0.0f, 0.0f},
            {1.0f, 0.0f, 0.0f},
            {-1.0f, 0.0f, 0.0f}, // Left face
            {-1.0f, 0.0f, 0.0f},
            {-1.0f, 0.0f, 0.0f},
            {-1.0f, 0.0f, 0.0f},
            {0.0f, 0.0f, 1.0f}, // Front face
            {0.0f, 0.0f, 1.0f},
            {0.0f, 0.0f, 1.0f},
            {0.0f, 0.0f, 1.0f},
            {0.0f, 0.0f, -1.0f}, // Back face
            {0.0f, 0.0f, -1.0f},
            {0.0f, 0.0f, -1.0f},
            {0.0f, 0.0f, -1.0f}
        };
        data.colors = {
            {1.0f, 0.0f, 0.0f, 1.0f}, // Red for bottom-left back corner
            {0.0f, 1.0f, 0.0f, 1.0f}, // Green for bottom-right back corner
            {0.0f, 0.0f, 1.0f, 1.0f}, // Blue for bottom-right front corner
            {1.0f, 1.0f, 0.0f, 1.0f}, // Yellow for bottom-left front corner
            {1.0f, 0.0f, 1.0f, 1.0f}, // Magenta for top-left back corner
            {0.0f, 1.0f, 1.0f, 1.0f}, // Cyan for top-right back corner
            {1.0f, 1.0f, 1.0f, 1.0f}, // White for top-right front corner
            {0.0f, 0.0f, 0.0f, 1.0f}  // Black for top-left front corner
        };
            
            uniformBuffer = vklCreateHostCoherentBufferWithBackingMemory(
                sizeof(UniformBuffer), VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
            );

            if (!load_from_obj("assets/meshes/arm.obj")) {
                throw std::runtime_error("Failed to load ronbotARM.obj");
            }
            //Determine on which shoulder arms should sit
            if("left" == side) mOffset =glm::vec3(0.1, 0, 0);
            if("right" == side) mOffset =glm::vec3(2.3, 0, 0);

            this->sceneObjectGeometry = createAndUploadIntoGpuMemory(this->data);

            ubo.ambientConstant = 0.1f; // Lower ambient reflection
            ubo.diffuseConstant = 0.2f; // Lower diffuse reflection
            ubo.specularConstant = 1.0f; // High specular reflection for metal
            ubo.shininessConstant = 64.0f;

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



                if (!material.diffuse_texname.empty()) {
                    std::string texture_path = "assets/textures/" + material.diffuse_texname;
                    ImageAndView texture = loadImage(dev, queue, cmdPool, texture_path, sync, pfn);

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

                    ImageAndView normalImageAndView = loadImage(device, queue, cmdPool, "assets/textures/RoundAluminiumPanel01_4K_Normal.dds", 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;
                }
            }

        
    }

    virtual void updateTransforms(const FrameTimer& frameTimer) 
    {
        glm::mat4 localTranslation = glm::translate(glm::mat4(1.0f), mOffset);
        glm::mat4 localRotation = glm::rotate(glm::mat4(1), -0.5f * std::abs(glm::sin(frameTimer.timePassedSinceStart)), glm::vec3(1,0,0));
        localTransform = localTranslation * localRotation;

        // Update the world transform by combining with the parent's world transform
        if (parent) {
            worldTransform = parent->getWorldTransform() * localTransform;
        } else {
            worldTransform = localTransform;
        }

        // Propagate the transform update to children
        for (auto& child : children) {
            child->updateTransforms(frameTimer);
        }
    }

    /**
     * Records the Vulkan rendering commands for the robot body 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){

        //if (frameTimer.timePassedSinceStart > 5.0f){
            drawObject(normalPipeline);      

            for (auto& child : children) {
                child->recordRenderingCommands(commandBuffer, hudPipeline, specularPipeline, normalPipeline, normalheightPipeline, frameTimer);
            }
        //}
    }


};


/**
 * Represents a the robot body object within a Vulkan-rendered scene.
 * Inherits from SceneObject to leverage common object properties and functionalities.
 */
class RobotObject : public SceneObjectWithPhysics{
    public:
        std::shared_ptr<RobotHeadObject> mHead;
        std::shared_ptr<RobotArm> mLeftArm;
        std::shared_ptr<RobotArm> mRightArm;
        bool  mIsStunned = false;
        /**
         * Constructor for the RobotBody.
         * 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.
         */
        RobotObject(VkDevice dev, 
            VkPhysicalDevice phyDev, 
            VkDescriptorPool descPool, 
            VkDescriptorSetLayout descSetLayout, 
            VkQueue queue, 
            Physics::GamePhysicsManager& inputPhysicsManager,
            glm::vec3 initialCollisionBoxRobot,
            VkSampler sampler,
            VkSampler normalSampler,
            VkCommandPool cmdPool,
            bool sync,
            PFN_vkCmdPipelineBarrier2KHR pfn,
            ShadowMap& shadowMap):
            mHead(std::make_shared<RobotHeadObject>(dev, phyDev, descPool, descSetLayout, queue, sampler, normalSampler, cmdPool, sync, pfn, shadowMap)),
            mLeftArm(std::make_shared<RobotArm>(dev, phyDev, descPool, descSetLayout, queue,"left", sampler, normalSampler, cmdPool, sync, pfn, shadowMap)),
            mRightArm(std::make_shared<RobotArm>(dev, phyDev, descPool, descSetLayout, queue, "right", sampler, normalSampler, cmdPool, sync, pfn, shadowMap)),
            SceneObjectWithPhysics(dev, phyDev, descPool, descSetLayout, queue, 
            {//Cube Vertex Positions
                {{-0.25f, -0.5f, -0.25f}}, // Bottom-left back corner
                {{0.25f, -0.5f, -0.25f}},  // Bottom-right back corner
                {{0.25f, -0.5f, 0.25f}},   // Bottom-right front corner
                {{-0.25f, -0.5f, 0.25f}},  // Bottom-left front corner
                {{-0.25f, 0.5f, -0.25f}}, // Top-left back corner
                {{0.25f, 0.5f, -0.25f}},  // Top-right back corner
                {{0.25f, 0.5f, 0.25f}},   // Top-right front corner
                {{-0.25f, 0.5f, 0.25f}},  // 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,            
            initialCollisionBoxRobot,        //inital position of collison box center 
            glm::vec3(1.25, 1.5, 1.25),  // initial half seidelngth of collision cylinder
            shadowMap,
            1.0f,                      // mass of robot
            Physics::ShapeType::CYLINDER)         // Collision Shape              
        {
            GeometryData data;
            data.positions = 
             {{-0.25f, -0.5f, -0.25f}, // Bottom-left back corner
                {0.25f, -0.5f, -0.25f},  // Bottom-right back corner
                {0.25f, -0.5f, 0.25f},   // Bottom-right front corner
                {-0.25f, -0.5f, 0.25f},  // Bottom-left front corner
                {-0.25f, 0.5f, -0.25f}, // Top-left back corner
                {0.25f, 0.5f, -0.25f},  // Top-right back corner
                {0.25f, 0.5f, 0.25f},   // Top-right front corner
                {-0.25f, 0.5f, 0.25f}}
                ;
            data.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 
        };
        data.normals = {
            // Normals for each face (flat shading)
            {0.0f, -1.0f, 0.0f}, // Bottom face
            {0.0f, -1.0f, 0.0f},
            {0.0f, -1.0f, 0.0f},
            {0.0f, -1.0f, 0.0f},
            {0.0f, 1.0f, 0.0f}, // Top face
            {0.0f, 1.0f, 0.0f},
            {0.0f, 1.0f, 0.0f},
            {0.0f, 1.0f, 0.0f},
            {1.0f, 0.0f, 0.0f}, // Right face
            {1.0f, 0.0f, 0.0f},
            {1.0f, 0.0f, 0.0f},
            {1.0f, 0.0f, 0.0f},
            {-1.0f, 0.0f, 0.0f}, // Left face
            {-1.0f, 0.0f, 0.0f},
            {-1.0f, 0.0f, 0.0f},
            {-1.0f, 0.0f, 0.0f},
            {0.0f, 0.0f, 1.0f}, // Front face
            {0.0f, 0.0f, 1.0f},
            {0.0f, 0.0f, 1.0f},
            {0.0f, 0.0f, 1.0f},
            {0.0f, 0.0f, -1.0f}, // Back face
            {0.0f, 0.0f, -1.0f},
            {0.0f, 0.0f, -1.0f},
            {0.0f, 0.0f, -1.0f}
        };
        data.colors = {
            {1.0f, 0.0f, 0.0f, 1.0f}, // Red for bottom-left back corner
            {0.0f, 1.0f, 0.0f, 1.0f}, // Green for bottom-right back corner
            {0.0f, 0.0f, 1.0f, 1.0f}, // Blue for bottom-right front corner
            {1.0f, 1.0f, 0.0f, 1.0f}, // Yellow for bottom-left front corner
            {1.0f, 0.0f, 1.0f, 1.0f}, // Magenta for top-left back corner
            {0.0f, 1.0f, 1.0f, 1.0f}, // Cyan for top-right back corner
            {1.0f, 1.0f, 1.0f, 1.0f}, // White for top-right front corner
            {0.0f, 0.0f, 0.0f, 1.0f}  // Black for top-left front corner
        };
            
            
            uniformBuffer = vklCreateHostCoherentBufferWithBackingMemory(
                sizeof(UniformBuffer), VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
            );

            if (!load_from_obj("assets/meshes/body.obj")) {
                throw std::runtime_error("Failed to load robot body.obj");
            }
            
            this->sceneObjectGeometry = createAndUploadIntoGpuMemory(this->data);

            ubo.ambientConstant = 0.1f; // Lower ambient reflection
            ubo.diffuseConstant = 0.2f; // Lower diffuse reflection
            ubo.specularConstant = 1.0f; // High specular reflection for metal
            ubo.shininessConstant = 64.0f;


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

                if (!material.diffuse_texname.empty()) {
                    std::string texture_path = "assets/textures/" + material.diffuse_texname;
                    ImageAndView texture = loadImage(dev, queue, cmdPool, texture_path, sync, pfn);

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

                    ImageAndView normalImageAndView = loadImage(device, queue, cmdPool, "assets/textures/RoundAluminiumPanel01_4K_Normal.dds", 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;
                }
            }



            std::shared_ptr<SceneNode>thisAsSharedPtr(this);
            mHead->setParent(thisAsSharedPtr);
            mLeftArm->setParent(thisAsSharedPtr);
            mRightArm->setParent(thisAsSharedPtr);
            addChild(mHead);
            addChild(mLeftArm);
            addChild(mRightArm);


            ubo.color = glm::vec4(1,0,0,1);
            mCollisionShapeExtents = glm::vec3(1.25f, 5.0, 1.25f);
            setObjectState(ObjectState::Pathing);
        
        }


        virtual void updateTransforms(const FrameTimer& frameTimer){


            if (frameTimer.timePassedSinceStart > 10.f){
                if (this->getObjectState() != ObjectState::Stunned){
                    if (isMoving){
                        updatePosition(frameTimer);
                    }
                } else {
                    if (stunStartTime < 0.0f) {
                        stunStartTime = frameTimer.timePassedSinceStart;
                    }

                    if (frameTimer.timePassedSinceStart - stunStartTime > 5.0f) {
                        this->setObjectState(ObjectState::NeedsUpdate);
                        stunStartTime = -1.0f;
                    }
                }

                if (this->getObjectState() == ObjectState::PhysicsControlled || this->getObjectState() == ObjectState::Stunned){
                    physicsObject->makeDynamic(physicsObject->mRigidBody);
                    worldTransform = physicsObject->getModelMatrix() * glm::scale(glm::mat4(1),glm::vec3(0.5,0.5,0.5));
                } else if (this->getObjectState() == ObjectState::Pathing) {
                    worldTransform = physicsObject->getModelMatrix()* glm::scale(glm::mat4(1), glm::vec3(0.5, 0.5, 0.5));
                }       

                // Propagate the transform update to children.
                for (auto& child : children) {
                    child->updateTransforms(frameTimer);
                }
            }

            
        }

        virtual ObjectState getRobotState(){
            return this->getObjectState();
        }

        virtual void setRobotState(){
            this->setObjectState(ObjectState::Pathing);
        }

    

    /**
     * Records the Vulkan rendering commands for the robot body 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){
        
        if (frameTimer.timePassedSinceStart > 10.0f){
            drawObject(normalPipeline);

            for (auto& child : children) {
                child->recordRenderingCommands(commandBuffer, hudPipeline, specularPipeline, normalPipeline, normalheightPipeline, frameTimer);
            }
       }
    }


    private:
    bool isMoving;
    float stunStartTime = -1.0f;


};

