#pragma once

#include "../libs/glm/glm/ext/matrix_float4x4.hpp"
#include "../libs/glm/glm/ext/vector_float2.hpp"

#include "brush.h"
#include "legacy_node_stroke_preview_draw_services.h"
#include "renderer_api/renderer_api.h"
#include "rtt.h"
#include "texture.h"

#include <array>
#include <cmath>
#include <cstdint>
#include <functional>
#include <memory>
#include <vector>

class Node;
class NodeStrokePreview;

namespace pp::panopainter {

[[nodiscard]] pp::renderer::RenderDeviceFeatures stroke_preview_render_device_features() noexcept;

void apply_legacy_node_stroke_preview_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height);
[[nodiscard]] pp::renderer::gl::OpenGlViewportRect query_legacy_node_stroke_preview_viewport();
[[nodiscard]] std::array<float, 4> query_legacy_node_stroke_preview_clear_color();
void apply_legacy_node_stroke_preview_clear_color(std::array<float, 4> color);
void apply_legacy_node_stroke_preview_scissor(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height);
void apply_legacy_node_stroke_preview_capability(std::uint32_t state, bool enabled);

void bind_legacy_node_stroke_preview_live_samplers(
    Sampler& mipmap_sampler,
    Sampler& linear_sampler,
    Sampler& repeat_sampler);
void bind_legacy_node_stroke_preview_dual_pass_textures(const Brush& dual_brush);
void bind_legacy_node_stroke_preview_pattern_texture(const Brush& brush);
void bind_legacy_node_stroke_preview_destination_texture(Texture2D& texture);
void unbind_legacy_node_stroke_preview_destination_texture(Texture2D& texture);
void unbind_legacy_node_stroke_preview_mixer_texture(RTT& mixer_rtt);
void copy_legacy_node_stroke_preview_destination_texture_region(
    int src_x,
    int src_y,
    int dst_x,
    int dst_y,
    int width,
    int height);

struct LegacyNodeStrokePreviewBackgroundCaptureRequest {
    glm::vec2 size {};
    bool colorize = false;
    Texture2D& background_texture;
    std::function<void()> draw_checkerboard;
};

void execute_legacy_node_stroke_preview_background_capture_pass(
    const LegacyNodeStrokePreviewBackgroundCaptureRequest& request);

struct LegacyNodeStrokePreviewStrokeComputeRequest {
    const Stroke& stroke;
    float zoom = 1.0f;
    glm::vec2 mixer_size {};
};

[[nodiscard]] std::vector<LegacyNodeStrokePreviewFrame> plan_legacy_node_stroke_preview_stroke_frames(
    const LegacyNodeStrokePreviewStrokeComputeRequest& request);

[[nodiscard]] std::vector<LegacyNodeStrokePreviewFrame> plan_legacy_node_stroke_preview_stroke_frames(
    const Stroke& stroke,
    float zoom,
    glm::vec2 mixer_size);

struct LegacyNodeStrokePreviewImmediateRuntimeRequest {
    const std::shared_ptr<Brush>& brush;
    glm::vec2 preview_size {};
    float zoom = 1.0f;
    float min_flow = 0.0f;
    float stroke_max_size_override = 0.0f;
    float pad_override = NAN;
    float camera_fov = 0.0f;
    glm::mat4 camera_rot { 1.0f };
    pp::renderer::RenderDeviceFeatures render_device_features {};
    RTT& preview_rtt;
    RTT& preview_rtt_mixer;
    Texture2D& preview_stroke_texture;
    Texture2D& preview_dual_texture;
    Texture2D& preview_background_texture;
    Texture2D& preview_image_texture;
    Sampler& linear_sampler;
    Sampler& repeat_sampler;
    std::function<void()> prepare_render_target;
    std::function<void()> finish_render_target;
    std::function<void(bool)> set_blend_enabled;
    std::function<void(const LegacyStrokeShaderSetupUniforms&)> setup_stroke_shader;
    std::function<void(const Brush&)> bind_dual_pass_textures;
    std::function<void(bool)> capture_background;
    std::function<std::vector<LegacyNodeStrokePreviewFrame>(const Stroke&, float)> compute_frames;
    std::function<glm::vec4(std::array<vertex_t, 4>&, Texture2D&, bool)> draw_samples;
    std::function<void(const glm::vec2&, const glm::vec2&)> draw_mix;
    std::function<void()> unbind_mixer_texture;
    std::function<void()> bind_pattern_texture;
    std::function<void()> draw_composite;
};

struct LegacyNodeStrokePreviewImmediateDrawRequest {
    const std::shared_ptr<Brush>& brush;
    glm::vec2 preview_size {};
    float zoom = 1.0f;
    float min_flow = 0.0f;
    float stroke_max_size_override = 0.0f;
    float pad_override = NAN;
    float camera_fov = 0.0f;
    glm::mat4 camera_rot { 1.0f };
    pp::renderer::RenderDeviceFeatures render_device_features {};
    RTT& preview_rtt;
    RTT& preview_rtt_mixer;
    Texture2D& preview_stroke_texture;
    Texture2D& preview_dual_texture;
    Texture2D& preview_background_texture;
    Texture2D& preview_image_texture;
    Sampler& linear_sampler;
    Sampler& repeat_sampler;
    std::function<void()> prepare_render_target;
    std::function<void()> finish_render_target;
    std::function<std::vector<LegacyNodeStrokePreviewFrame>(const Stroke&, float)> compute_frames;
    std::function<glm::vec4(std::array<vertex_t, 4>&, Texture2D&, bool)> draw_samples;
    std::function<void(const glm::vec2&, const glm::vec2&)> draw_mix;
    std::function<void()> draw_checkerboard;
    std::function<void()> draw_composite;
};

[[nodiscard]] bool execute_legacy_node_stroke_preview_immediate_draw(
    const LegacyNodeStrokePreviewImmediateDrawRequest& request);

[[nodiscard]] bool execute_legacy_node_stroke_preview_immediate_runtime(
    const LegacyNodeStrokePreviewImmediateRuntimeRequest& request);

void initialize_legacy_node_stroke_preview_clone(Node* dest);
[[nodiscard]] bool execute_legacy_node_stroke_preview_immediate_draw(NodeStrokePreview& preview);

} // namespace pp::panopainter
