#pragma once

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

GeometryData createCircleGeometry(float widthOverHeigth, const glm::vec3& inputCenter = glm::vec3(0.75, -0.9, 0.0), const float r = 0.2, const uint32_t& numberOfVertices = 60)
{
    glm::vec3 center = inputCenter;
    center.y = center.y / widthOverHeigth;
    // first vertex is the center
    GeometryData data;
    float pi = 3.14156;

    glm::vec3 nullVec3(0, 0, 0);
    glm::vec4 nullVec4(0, 0, 0, 0);
    data.positions.push_back(center);
    data.normals.push_back(nullVec3);
    data.textureCoordinates.push_back(glm::vec2(0.5, 0.5));
    data.tangents.push_back(nullVec3);
    

    // Add "numberOfVertices" many vertices around circle  starting at 3 oClock
    for (int i = 0; i < numberOfVertices; i++)
    {
       float angle = ((float)i) * 2 * pi / (float)numberOfVertices;
       glm::vec2 textureCoordinates = glm::vec2(0.5,0.5) + 0.5f * glm::vec2(glm::cos(angle), glm::sin(angle));
       data.positions.push_back(center + r * glm::vec3(glm::cos(angle), widthOverHeigth * glm::sin(angle), 0));
       data.normals.push_back(nullVec3);
       data.textureCoordinates.push_back(textureCoordinates);
       data.tangents.push_back(nullVec3);
    }


    // Create indices for  all "slices" except the last one
    for (int i = 1; i < numberOfVertices; i++)
    {
        data.indices.push_back(0);
        data.indices.push_back(i);
        data.indices.push_back(i + 1);
    }

    // Create indices for last slice to close the circle
    data.indices.push_back(0);
    data.indices.push_back(numberOfVertices);
    data.indices.push_back(1);

    // Return complete data
    return data;
}


class HudObject : public SceneObject{
    public:
        std::vector<TextureData> m_textureData;
        std::vector<VkDescriptorSet> m_descriptorSets;

        HudObject(VkDevice dev, VkPhysicalDevice phyDev, VkDescriptorPool descPool, VkDescriptorSetLayout descSetLayout, VkQueue queue, ShadowMap& shadowMap, glm::vec2 screenRes, TextureLoader& textureLoader)
        : SceneObject(dev, phyDev, descPool, descSetLayout, queue,  { {{-0.2f,  0.2f, 0.0f}}, {{-0.2f, -0.2f, 0.0f}}, {{ 0.2f,  0.2f, 0.0f}}, {{ 0.2f, -0.2f, 0.0f}} }, {0, 1, 2, 1, 3, 2}, 
            sampler, cmdPool, sync, pfn, shadowMap)
        {
            GeometryData data = createCircleGeometry(screenRes.x / screenRes.y);
            sceneObjectGeometry = createAndUploadIntoGpuMemory(data);
            uniformBuffer = vklCreateHostCoherentBufferWithBackingMemory(
                sizeof(UniformBuffer), VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
            );
            //descriptorSet = allocDescriptorSet(dev, descPool, descSetLayout);
            //writeDescriptorSet(dev, descriptorSet, uniformBuffer);
            worldTransform = glm::mat4(1.0f); // identity
            localTransform = glm::mat4(1.0f);

            // Create a 5 descriptorsets (one for every pizza texture)
            // i is the number of pizzas on plate!
            for (int i = 1; i <= 5; i++)
            {
                // Load texture data into provided structs
                m_textureData.push_back(TextureData(dev));
                std::string filePath = "assets/textures/" + std::to_string(i) + "pizza.jpg";
                textureLoader.load(filePath, m_textureData.back());

                // Create own descriptorset for current texture
                m_descriptorSets.push_back(allocDescriptorSet(dev, descPool, descSetLayout));
                writeDescriptorSet(dev, m_descriptorSets.back(), uniformBuffer);
                writeDescriptorSet(dev, m_descriptorSets.back(), m_textureData.back().textureImageView, m_textureData.back().textureSampler, 1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
            }
            descriptorSet = m_descriptorSets[4];
        }

    /**
     * Updates the uniform buffer with transformation matrices and other data.
     * This function should be called every frame to update shader data based on transformations.
     */
    virtual void updateUniformBuffer(Student::CameraForGLFW& camera, const glm::vec4& windowSize, const FrameTimer& frameTimer) 
    {
        ubo.modelMatrix = glm::mat4(1.0f);  // This trafo controls how big the trnasparent quad is. it should be small .
        ubo.modelViewProjMatrix = glm::mat4(1.0f);  // Combine with the camera matrix
        ubo.userInput = glm::ivec4(0, windowSize.x, windowSize.y, 0 ); // last index tells that this renders pizza inidicator
        vklCopyDataIntoHostCoherentBuffer(this->uniformBuffer, &ubo, sizeof(ubo));
    }

    virtual void updateUniformBufferForShadow(ShadowMap shadowMap, Spotlight spotlight)
    {
        // THis object does not have a shadow
    }

    void recordRenderingCommands(VkCommandBuffer commandBuffer, VkPipeline hudPipeline, VkPipeline specularPipeline, VkPipeline normalPipeline, VkPipeline normalheightPipeline, const FrameTimer& frameTimer){
    
        drawGeometryWithMaterial(hudPipeline, this->sceneObjectGeometry, descriptorSet);
    }

    virtual void recordRenderingCommandsForShadowMap(ShadowMap& shadowMap, const Spotlight& spotlight)
    {
        // The screen quad is meant to render the aiming cross and does not throw a shadow iteself
    }

    void updateRemainingPizzas(const uint32_t& numberOfRemainingPizzas)
    {
        if (numberOfRemainingPizzas > 0)
        {
            const uint32_t idx = numberOfRemainingPizzas < m_descriptorSets.size() ? numberOfRemainingPizzas - 1 : 0;
            descriptorSet = m_descriptorSets[idx];
        }
    }


};

