#pragma once

#include "MayaFlux/Buffers/VKBuffer.hpp"
#include "MayaFlux/Nodes/Graphics/TextureNode.hpp"
#include "NodeTextureProcessor.hpp"

namespace MayaFlux::Buffers {

class RenderProcessor;

/**
 * @class NodeTextureBuffer
 * @brief Staging buffer for uploading TextureNode pixel data to GPU textures
 *
 * Bridges CPU-side texture generation (TextureNode) to GPU-resident textures (VKImage).
 * The VKBuffer part serves as staging memory for efficient CPU→GPU transfers.
 *
 * Philosophy:
 * - Textures are GENERATED by nodes, not loaded from files
 * - Data flows: Node::compute_frame() → staging buffer → GPU VKImage
 * - Supports both single and multiple texture node bindings
 * - Clean separation: staging (VKBuffer) vs target (VKImage)
 *
 * Usage (single texture):
 *   auto noise_node = std::make_shared<PerlinNoiseNode>(512, 512);
 *   auto buffer = std::make_shared<NodeTextureBuffer>(noise_node, "perlin");
 *   buffer->setup_processors(Graphics);
 *
 *   // Access GPU texture for shader binding:
 *   auto texture = buffer->get_gpu_texture();
 *   render_processor->bind_texture("perlin", texture);
 *
 * Usage (multiple textures via processor):
 *   auto staging = std::make_shared<NodeTextureBuffer>(primary_node, "primary");
 *   auto processor = staging->get_texture_processor();
 *
 *   processor->bind_texture_node("secondary", secondary_node, secondary_texture);
 *   processor->bind_texture_node("tertiary", tertiary_node, tertiary_texture);
 *
 *   staging->setup_processors(Graphics);
 *   // Now all three textures upload through shared staging buffer
 */
class MAYAFLUX_API NodeTextureBuffer : public VKBuffer {
public:
    /**
     * @brief Create texture staging buffer from generative node
     * @param node TextureNode that generates pixels each frame
     * @param binding_name Logical name for this texture binding (default: "texture")
     *
     * Buffer size is automatically calculated from texture dimensions:
     * width * height * 4 channels * sizeof(float) = RGBA32F format
     */
    explicit NodeTextureBuffer(
        std::shared_ptr<Nodes::GpuSync::TextureNode> node,
        std::string binding_name = "texture");

    ~NodeTextureBuffer() override = default;

    /**
     * @brief Initialize the buffer, create GPU texture, and setup processors
     */
    void setup_processors(ProcessingToken token) override;

    /**
     * @brief Setup rendering with RenderProcessor
     * @param config Rendering configuration
     *
     * Configures the buffer for direct rendering to screen.
     * The GPU texture will be bound and drawn automatically.
     */
    void setup_rendering(const RenderConfig& config);

    /**
     * @brief Get the GPU-resident texture (VKImage)
     * @return Shared pointer to VKImage suitable for shader binding
     */
    [[nodiscard]] std::shared_ptr<Core::VKImage> get_gpu_texture() const
    {
        return m_gpu_texture;
    }

    /**
     * @brief Get the texture node driving this buffer
     */
    [[nodiscard]] std::shared_ptr<Nodes::GpuSync::TextureNode> get_texture_node() const
    {
        return m_texture_node;
    }

    /**
     * @brief Get the processor managing uploads
     * @return Processor that can bind additional texture nodes
     */
    [[nodiscard]] std::shared_ptr<NodeTextureProcessor> get_texture_processor() const
    {
        return m_texture_processor;
    }

    /**
     * @brief Get the logical binding name
     */
    [[nodiscard]] const std::string& get_binding_name() const
    {
        return m_binding_name;
    }

    /**
     * @brief Get texture dimensions from node
     */
    [[nodiscard]] std::pair<uint32_t, uint32_t> get_dimensions() const
    {
        return m_texture_node
            ? std::make_pair(m_texture_node->get_width(), m_texture_node->get_height())
            : std::make_pair(0U, 0U);
    }

    /**
     * @brief Trigger pixel computation on the node
     *
     * Calls node->compute_frame() to regenerate pixels.
     * Useful for explicit frame updates when not using domain-driven processing.
     */
    void update_texture()
    {
        if (m_texture_node) {
            m_texture_node->compute_frame();
        }
    }

    void generate_fullscreen_quad();

    size_t calculate_quad_vertex_size();

private:
    std::shared_ptr<Nodes::GpuSync::TextureNode> m_texture_node;
    std::shared_ptr<Core::VKImage> m_gpu_texture;
    std::shared_ptr<NodeTextureProcessor> m_texture_processor;
    std::string m_binding_name;

    std::vector<uint8_t> m_vertex_bytes;

    /**
     * @brief Calculate staging buffer size from node dimensions
     * @note Assumes RGBA32F format (4 channels * sizeof(float))
     */
    static size_t calculate_buffer_size(
        const std::shared_ptr<Nodes::GpuSync::TextureNode>& node);

    friend class NodeTextureProcessor;
};

} // namespace MayaFlux::Buffers
