#pragma once
#include "../entity_manager.h"
#include "../components/render_component.h"
#include "../components/transform_component.h"
#include "../components/shader_component.h"
#include "shader_manager_class.h"
#include <DirectXMath.h>

namespace ecs {

class RenderSystem {
public:
    /**
     * Builder for the RenderSystem class.
     * This class is responsible for rendering entities with the necessary components.
     * @param deviceContext 
     * @param shaderManager 
     */
    RenderSystem(ID3D11DeviceContext* deviceContext, shader_manager_class* shaderManager) 
        : m_deviceContext(deviceContext), m_shaderManager(shaderManager) {}
    
    /**
     * Render an entity with the necessary components.
     * This method checks if the entity has the required components and renders it using the appropriate shader.
     * @param entity The entity to render.
     * @param viewMatrix The view matrix for rendering.
     * @param projectionMatrix The projection matrix for rendering.
     * @param diffuseColors Array of diffuse colors for lighting.
     * @param lightPositions Array of light positions for lighting.
     * @param ambientColors Array of ambient colors for lighting.
     * @param cameraPosition The position of the camera in world space.
     * @param sunlightDiffuse The diffuse color of sunlight.
     * @param sunlightAmbient The ambient color of sunlight.
     * @param sunlightDirection The direction of sunlight in world space.
     * @param sunlightIntensity The intensity of sunlight.
     * @return True if rendering was successful, false otherwise.
     */
    bool RenderEntity(std::shared_ptr<Entity> entity, 
                      const DirectX::XMMATRIX& viewMatrix, 
                      const DirectX::XMMATRIX& projectionMatrix,
                      const DirectX::XMFLOAT4* diffuseColors,
                      const DirectX::XMFLOAT4* lightPositions,
                      const DirectX::XMFLOAT4* ambientColors,
                      const DirectX::XMFLOAT3& cameraPosition,
                      const DirectX::XMFLOAT4& sunlightDiffuse,
                      const DirectX::XMFLOAT4& sunlightAmbient,
                      const DirectX::XMFLOAT3& sunlightDirection,
                      float sunlightIntensity) {
        
        // Vérifier si l'entité a tous les composants nécessaires
        auto transform = entity->GetComponent<TransformComponent>();
        auto render = entity->GetComponent<RenderComponent>();
        auto shader = entity->GetComponent<ShaderComponent>();

        if (!transform || !render || !shader || !render->GetModel())
            R_FALSE

        // Calculer la matrice monde
        XMMATRIX scaleMatrix = transform->GetScaleMatrix();
        XMMATRIX rotateMatrix = transform->GetRotateMatrix();
        XMMATRIX translateMatrix = transform->GetTranslateMatrix();

        XMMATRIX worldMatrix = XMMatrixMultiply(
            XMMatrixMultiply(scaleMatrix, rotateMatrix),
            translateMatrix
        );

        // Rendre le modèle
        render->Render(m_deviceContext);

        // Sélectionner le shader approprié
        switch (shader->GetActiveShader()) {
            case ShaderType::ALPHA_MAPPING:
                return m_shaderManager->render_alpha_map_shader(
                    m_deviceContext,
                    render->GetIndexCount(),
                    worldMatrix,
                    viewMatrix,
                    projectionMatrix,
                    render->GetTexture(TextureType::Diffuse, 0),
                    render->GetTexture(TextureType::Diffuse, 1),
                    render->GetTexture(TextureType::Alpha, 0)
                );

            case ShaderType::CEL_SHADING:
                return m_shaderManager->render_cel_shading_shader(
                    m_deviceContext,
                    render->GetIndexCount(),
                    worldMatrix,
                    viewMatrix,
                    projectionMatrix,
                    render->GetTexture(TextureType::Diffuse, 0),
                    sunlightDiffuse,
                    sunlightAmbient,
                    sunlightDirection,
                    sunlightIntensity
                );

            case ShaderType::NORMAL_MAPPING:
                return m_shaderManager->render_normal_map_shader(
                    m_deviceContext,
                    render->GetIndexCount(),
                    worldMatrix,
                    viewMatrix,
                    projectionMatrix,
                    render->GetTexture(TextureType::Diffuse, 0),
                    render->GetTexture(TextureType::Normal, 0),
                    sunlightDirection,
                    sunlightDiffuse
                );

            case ShaderType::SPECULAR_MAPPING:

                return m_shaderManager->render_spec_map_shader(
                    m_deviceContext,
                    render->GetIndexCount(),
                    worldMatrix,
                    viewMatrix,
                    projectionMatrix,
                    render->GetTexture(TextureType::Diffuse, 0),
                    render->GetTexture(TextureType::Normal, 0),
                    render->GetTexture(TextureType::Specular, 0),
                    sunlightDirection,
                    sunlightDiffuse,
                    cameraPosition,
                    sunlightDiffuse,        // Couleur speculaire (à ajuster)
                    16.0f                   // Puissance speculaire (à ajuster)
                );

            case ShaderType::LIGHTING:
                {
                    // Créer des copies locales non constantes des tableaux
                    DirectX::XMFLOAT4 localDiffuseColors[4];
                    DirectX::XMFLOAT4 localLightPositions[4];
                    DirectX::XMFLOAT4 localAmbientColors[4];
                    
                    // Copier les données
                    for (int i = 0; i < 4; i++) {
                        localDiffuseColors[i] = diffuseColors[i];
                        localLightPositions[i] = lightPositions[i];
                        localAmbientColors[i] = ambientColors[i];
                    }
                    
                    return m_shaderManager->renderlight_shader(
                        m_deviceContext,
                        render->GetIndexCount(),
                        worldMatrix,
                        viewMatrix,
                        projectionMatrix,
                        render->GetTexture(TextureType::Diffuse, 0),
                        localDiffuseColors,
                        localLightPositions,
                        localAmbientColors
                    );
                }

            case ShaderType::SUNLIGHT:
                return m_shaderManager->render_sunlight_shader(
                    m_deviceContext,
                    render->GetIndexCount(),
                    worldMatrix,
                    viewMatrix,
                    projectionMatrix,
                    render->GetTexture(TextureType::Diffuse, 0),
                    sunlightDiffuse,
                    sunlightAmbient,
                    sunlightDirection,
                    sunlightIntensity
                );

            case ShaderType::SKYBOX:
                return m_shaderManager->render_skybox_shader(
                    m_deviceContext,
                    render->GetIndexCount(),
                    worldMatrix,
                    viewMatrix,
                    projectionMatrix,
                    render->GetTexture(TextureType::Diffuse, 0),
                    sunlightDiffuse,
                    sunlightAmbient,
                    sunlightDirection,
                    sunlightIntensity
                );

            case ShaderType::TEXTURE:
            default:
                return m_shaderManager->render_texture_shader(
                    m_deviceContext,
                    render->GetIndexCount(),
                    worldMatrix,
                    viewMatrix,
                    projectionMatrix,
                    render->GetTexture(TextureType::Diffuse, 0)
                );
        }
    }

    /**
     * Render all entities in the EntityManager that have the necessary components.
     * This method iterates through all entities and renders them if they have the required components.
     * @param entityManager The EntityManager containing the entities to render.
     * @param viewMatrix The view matrix for rendering.
     * @param projectionMatrix The projection matrix for rendering.
     * @param diffuseColors Array of diffuse colors for lighting.
     * @param lightPositions Array of light positions for lighting.
     * @param ambientColors Array of ambient colors for lighting.
     * @param cameraPos The position of the camera in world space.
     * @param sunlightDiffuse The diffuse color of sunlight.
     * @param sunlightAmbient The ambient color of sunlight.
     * @param sunlightDirection The direction of sunlight in world space.
     * @param sunlightIntensity The intensity of sunlight.
     * @return The number of entities rendered successfully.
     */
    int RenderAllEntities(EntityManager* entityManager,
                         const DirectX::XMMATRIX& viewMatrix,
                         const DirectX::XMMATRIX& projectionMatrix,
                         const DirectX::XMFLOAT4* diffuseColors,
                         const DirectX::XMFLOAT4* lightPositions,
                         const DirectX::XMFLOAT4* ambientColors,
                         const DirectX::XMFLOAT3& cameraPos,
                         const DirectX::XMFLOAT4& sunlightDiffuse,
                         const DirectX::XMFLOAT4& sunlightAmbient,
                         const DirectX::XMFLOAT3& sunlightDirection,
                         float sunlightIntensity) {
        
        int renderCount = 0;

        // Récupérer toutes les entités qui ont les composants RenderComponent et TransformComponent
        auto entities = entityManager->GetEntitiesWithComponent<RenderComponent>();
        
        for (auto& entity : entities) {
            auto render = entity->GetComponent<RenderComponent>();
            
            // Vérifier si l'entité a un TransformComponent
            auto transform = entity->GetComponent<TransformComponent>();
            if (!transform) continue;

            // Vérifier si le modèle est visible
            if (!render->IsVisible()) continue;

            // check if the id the sky id to disabled the z buffer
            if (entity->GetID() == entityManager->GetSkyID())
            {
                // Désactiver le Z-buffer pour le skysphere
                m_deviceContext->OMSetDepthStencilState(nullptr, 0);
            } else {
                // Activer le Z-buffer pour les autres entités
                m_deviceContext->OMSetDepthStencilState(nullptr, 1);
            }

            // Effectuer le rendu
            if (RenderEntity(entity, viewMatrix, projectionMatrix, 
                            diffuseColors, lightPositions, ambientColors,cameraPos,
                            sunlightDiffuse, sunlightAmbient, sunlightDirection,
                            sunlightIntensity)) {
                renderCount++;
            }
        }

        return renderCount;
    }

private:
    ID3D11DeviceContext* m_deviceContext;
    shader_manager_class* m_shaderManager;
};

} // namespace ecs
