#pragma once

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


/**
 * Represents a house object within a Vulkan-rendered scene.
 * Inherits from SceneObject to leverage common object properties and functionalities.
 */
class SpaceshipObject : public SceneObject{
    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.
     */
        SpaceshipObject(VkDevice dev, 
            VkPhysicalDevice phyDev, 
            VkDescriptorPool descPool, 
            VkDescriptorSetLayout descSetLayout, 
            VkQueue queue, 
            VkSampler sampler,
            VkSampler specularSampler,
            VkSampler normalSampler,
            VkCommandPool cmdPool,
            bool sync,
            PFN_vkCmdPipelineBarrier2KHR pfn,
            ShadowMap& shadowMap,
            const float& inputMass = 0.f,
            const glm::vec3 startPosition = glm::vec3(0.0f, 5.0f, 0.0f),
            const glm::mat4 finalTranslation = glm::mat4(1.0f),
            const float& animationDuration = 10.0f, 
            const bool& hasStoppedMoving = false):
            SceneObject(dev, phyDev, descPool, descSetLayout, queue, 
            {
                // Vertices for the top and bottom face of the pizza box
                {{-0.1f, 0.1f, 0.251f}, {1.0f, 0.0f, 0.0f}},  // Top face top-left corner
                {{0.1f, 0.1f, 0.251f}, {0.0f, 1.0f, 0.0f}},   // Top face top-right corner
                {{0.1f, 0.1f, -0.251f}, {0.0f, 0.0f, 1.0f}},  // Top face bottom-right corner
                {{-0.1f, 0.1f, -0.251f}, {1.0f, 1.0f, 1.0f}}, // Top face bottom-left corner
                {{-0.1f, -0.1f, 0.251f}, {1.0f, 1.0f, 0.0f}}, // Bottom face top-left corner
                {{0.1f, -0.1f, 0.251f}, {0.0f, 0.0f, 0.0f}},  // Bottom face top-right corner
                {{0.1f, -0.1f, -0.251f}, {1.0f, 0.0f, 1.0f}}, // Bottom face bottom-right corner
                {{-0.1f, -0.1f, -0.251f}, {0.0f, 1.0f, 1.0f}} // Bottom face bottom-left corner
            },
            // Indices for drawing the box with triangles
            {
                0, 1, 2, 0, 2, 3, // Top face
                4, 5, 6, 4, 6, 7, // Bottom face
                0, 4, 5, 0, 5, 1, // Side faces
                1, 5, 6, 1, 6, 2,
                2, 6, 7, 2, 7, 3,
                3, 7, 4, 3, 4, 0
            },
            sampler,
            cmdPool,
            sync,
            pfn,
            shadowMap)
        {

            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, 10.0f, 0.0f));

            

        uniformBuffer = vklCreateHostCoherentBufferWithBackingMemory(
            sizeof(UniformBuffer), VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
        );
        if (!load_from_obj("assets/meshes/UFO.obj")) {
            throw std::runtime_error("Failed to load ufo.obj");
        }

        applyTransformations(this->data.positions, rotationMatrix, scalingMatrix, translationMatrix);
        this->sceneObjectGeometry = createAndUploadIntoGpuMemory(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 = "assets/textures/" + material.diffuse_texname;
                ImageAndView texture = loadImage(dev, queue, cmdPool, texture_path, sync, pfn);

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


                ImageAndView specularImageAndView = loadImage(device, queue, cmdPool, "assets/textures/UFO_Metal_Metallic.dds", sync, pfn);

                ImageAndView normalImageAndView = loadImage(device, queue, cmdPool, "assets/textures/UFO_Metal_Normal.dds", sync, pfn);
                writeDescriptorSet(device, descriptorSet, uniformBuffer, texture.view, sampler,  specularImageAndView.view, specularSampler, 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;

            }
         }
        

    }

    /**
     * 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(specularPipeline);

    }

    virtual void updateTransforms(const FrameTimer& frameTimer) override {
        
        glm::mat4 localTranslation = glm::mat4(1.0f);

        if (frameTimer.timePassedSinceStart < 7.0f) {
            localTranslation = glm::translate(glm::mat4(1.0f), glm::vec3(-2, 1, 4) * frameTimer.timePassedSinceStart);
            finalTranslation = localTranslation;
        } else {
            hasStoppedMoving = true;
        }


        glm::mat4 localRotation = glm::rotate(glm::mat4(1.0f), frameTimer.timePassedSinceStart, glm::vec3(0, 1, 0));


        if (hasStoppedMoving) {
            localTransform = finalTranslation * localRotation;
        } else {
            localTransform = localTranslation * localRotation;
        }

        if (parent) {
            worldTransform = parent->getWorldTransform() * localTransform;
        } else {
            worldTransform = localTransform;
        }

}


private:
bool hasStoppedMoving;
glm::mat4 finalTranslation;

};

    
    

