#pragma once
#include "Mesh.h"
#include "Shader.h"
#include <vector>
#include <memory>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

// Forward declarations
class Camera;

// Modern scene management using C++23
class Scene {
public:
    // Scene node using modern C++ features
    struct SceneNode {
        std::shared_ptr<Mesh> mesh;
        glm::mat4 transform{1.0f};
        glm::vec3 position{0.0f};
        glm::vec3 rotation{0.0f}; // Euler angles in degrees
        glm::vec3 scale{1.0f};
        bool visible = true;
        
        // Update transform matrix from position, rotation, scale
        void UpdateTransform();
        
        // C++20 spaceship operator
        auto operator<=>(const SceneNode& other) const = default;
    };

    // Light structure
    struct Light {
        enum class Type { Directional, Point, Spot };
        
        Type type = Type::Directional;
        glm::vec3 position{0.0f};
        glm::vec3 direction{0.0f, -1.0f, 0.0f};
        glm::vec3 color{1.0f};
        float intensity = 1.0f;
        
        // Point/Spot light attenuation
        float constant = 1.0f;
        float linear = 0.09f;
        float quadratic = 0.032f;
        
        // Spot light specific
        float inner_cutoff = 12.5f; // degrees
        float outer_cutoff = 15.0f; // degrees
        
        bool enabled = true;
    };

    Scene() = default;
    ~Scene() = default;

    // Delete copy constructor and assignment
    Scene(const Scene&) = delete;
    Scene& operator=(const Scene&) = delete;

    // Move semantics
    Scene(Scene&&) noexcept = default;
    Scene& operator=(Scene&&) noexcept = default;

    // Node management
    size_t AddNode(std::shared_ptr<Mesh> mesh, const glm::mat4& transform = glm::mat4(1.0f));
    size_t AddNode(std::shared_ptr<Mesh> mesh, const glm::vec3& position, 
                   const glm::vec3& rotation = glm::vec3(0.0f), 
                   const glm::vec3& scale = glm::vec3(1.0f));
    
    void RemoveNode(size_t index);
    void SetNodeTransform(size_t index, const glm::mat4& transform);
    void SetNodePosition(size_t index, const glm::vec3& position);
    void SetNodeRotation(size_t index, const glm::vec3& rotation);
    void SetNodeScale(size_t index, const glm::vec3& scale);
    void SetNodeVisibility(size_t index, bool visible);

    // Light management  
    size_t AddDirectionalLight(const glm::vec3& direction, const glm::vec3& color = glm::vec3(1.0f), float intensity = 1.0f);
    size_t AddPointLight(const glm::vec3& position, const glm::vec3& color = glm::vec3(1.0f), float intensity = 1.0f);
    size_t AddSpotLight(const glm::vec3& position, const glm::vec3& direction, 
                       const glm::vec3& color = glm::vec3(1.0f), float intensity = 1.0f,
                       float inner_cutoff = 12.5f, float outer_cutoff = 15.0f);
    
    void RemoveLight(size_t index);
    void SetLightEnabled(size_t index, bool enabled);

    // Rendering
    void Render(Shader& shader, const glm::mat4& view_matrix, const glm::mat4& projection_matrix);
    void RenderShadowMap(Shader& shader, const glm::mat4& lightSpace);


    // Utility functions
    void Clear();
    [[nodiscard]] size_t GetNodeCount() const { return nodes_.size(); }
    [[nodiscard]] size_t GetLightCount() const { return lights_.size(); }
    [[nodiscard]] const SceneNode& GetNode(size_t index) const { return nodes_[index]; }
    [[nodiscard]] const Light& GetLight(size_t index) const { return lights_[index]; }

    // Debug functions
    void PrintSceneInfo() const;

    // Batch operations using ranges (C++20)
    void SetAllNodesVisibility(bool visible);
    void UpdateAllNodeTransforms();
    
    // Culling and optimization
    void FrustumCull(const glm::mat4& view_projection_matrix);
    [[nodiscard]] size_t GetVisibleNodeCount() const;

private:
    std::vector<SceneNode> nodes_;
    std::vector<Light> lights_;
    std::vector<bool> node_visibility_cache_; // For frustum culling

    void SetupLightUniforms(const Shader& shader) const;
    [[nodiscard]] bool IsNodeInFrustum(const SceneNode& node, const glm::mat4& view_projection_matrix) const;
};