#pragma once
#include "node.h"
#include "node_stroke_preview.h"
#include "node_slider.h"
#include "brush.h"
#include "node_checkbox.h"
#include "node_combobox.h"
#include "node_button_custom.h"
#include "node_image.h"
#include "node_panel_brush.h"
#include "ui_core/node_lifetime.h"

namespace pp::app {
struct BrushStrokeControlPlan;
enum class BrushStrokeBoolSetting;
enum class BrushStrokeFloatSetting;
enum class BrushStrokeBlendSetting;
} // namespace pp::app

class NodePanelStroke : public Node
{
public:
    NodeStrokePreview* m_preview;
    NodeComboBox* m_blend_mode;
    NodeSliderH* m_tip_size;
    NodeSliderH* m_tip_spacing;
    NodeSliderH* m_tip_flow;
    NodeSliderH* m_tip_opacity;
    NodeSliderH* m_tip_angle;
    NodeSliderH* m_tip_angle_smooth;
    NodeSliderH* m_tip_mix;
    NodeSliderH* m_tip_wet;
    NodeSliderH* m_tip_noise;
    NodeSliderH* m_tip_hue;
    NodeSliderH* m_tip_sat;
    NodeSliderH* m_tip_val;
    NodeSliderH* m_jitter_scale;
    NodeSliderH* m_jitter_angle;
    NodeSliderH* m_jitter_scatter;
    NodeSliderH* m_jitter_flow;
    NodeSliderH* m_jitter_opacity;
    NodeSliderH* m_jitter_hue;
    NodeSliderH* m_jitter_sat;
    NodeSliderH* m_jitter_val;
    NodeCheckBox* m_jitter_hsv_eachsample;
    NodeSliderH* m_jitter_aspect;
    NodeCheckBox* m_tip_angle_init;
    NodeCheckBox* m_tip_angle_follow;
    NodeCheckBox* m_tip_flow_pressure;
    NodeCheckBox* m_tip_opacity_pressure;
    NodeCheckBox* m_tip_size_pressure;
    NodeCheckBox* m_jitter_scatter_bothaxis;
    NodeCheckBox* m_jitter_aspect_bothaxis;
    NodeButtonCustom* m_brush_button;
    NodeButtonCustom* m_dual_brush_button;
    NodeButtonCustom* m_pattern_button;
    NodeImage* m_brush_thumb;
    NodeImage* m_dual_brush_thumb;
    NodeImage* m_pattern_thumb;
    NodeButtonCustom* m_preset_button;

    NodeCheckBox* m_tip_invert;
    NodeCheckBox* m_tip_flipx;
    NodeCheckBox* m_tip_flipy;
    NodeCheckBox* m_pattern_enabled;
    NodeCheckBox* m_dual_enabled;
    NodeCheckBox* m_dual_scatter_bothaxis;
    NodeCheckBox* m_dual_invert;
    NodeCheckBox* m_dual_flipx;
    NodeCheckBox* m_dual_flipy;
    NodeCheckBox* m_dual_randflip;
    NodeCheckBox* m_tip_randflipx;
    NodeCheckBox* m_tip_randflipy;
    NodeSliderH*  m_dual_size;
    NodeSliderH*  m_dual_spacing;
    NodeSliderH*  m_dual_scatter;
    NodeSliderH*  m_tip_aspect;
    NodeSliderH*  m_dual_flow;
    NodeSliderH*  m_dual_opacity;
    NodeSliderH*  m_dual_rotate;
    NodeComboBox* m_dual_blend_mode;
    NodeComboBox* m_pattern_blend_mode;
    NodeButtonCustom* m_tip_aspect_reset;

    NodeCheckBox* m_pattern_eachsample;
    NodeCheckBox* m_pattern_invert;
    NodeCheckBox* m_pattern_flipx;
    NodeCheckBox* m_pattern_flipy;
    NodeCheckBox* m_pattern_rand_offset;
    NodeSliderH* m_pattern_scale;
    NodeSliderH* m_pattern_brightness;
    NodeSliderH* m_pattern_contrast;
    NodeSliderH* m_pattern_depth;

    NodeButton* m_brush_settings_reset;

    std::shared_ptr<NodePanelBrush> m_brush_popup;
    std::shared_ptr<NodePanelBrush> m_pattern_popup;
    std::function<void(Node* target)> on_stroke_change;
    std::function<void(Node* target, const std::string& path, const std::string& thumb)> on_pattern_changed;
    std::function<void(Node* target, const std::string& path, const std::string& thumb)> on_brush_changed;
    std::function<void(Node* target, const std::string& path, const std::string& thumb)> on_dual_changed;
    //std::function<void(Node* target, const std::string& path, const std::string& thumb)> on_texture_changed;
    pp::ui::NodeHandle m_popup_overlay_handle {};
    pp::ui::NodeHandle m_tick_overlay_handle {};

    struct SliderCurve
    {
        std::function<float(float)> m_fn;
        std::function<float(float)> m_inv;
        inline float to_value(float v) { return m_fn(v); }
        inline float to_slider(float v) { return m_inv(v); }
    };
    std::map<NodeSliderH*, SliderCurve> m_curves;
    int m_default_brush_index = 0;
    
    virtual Node* clone_instantiate() const override;
    virtual void clone_finalize(Node* dest) const override;
    virtual void init() override;
    virtual kEventResult handle_event(Event* e) override;

    void init_controls();
    void update_controls();

    void set_flow(float value, bool normalized, bool propagate);
    void set_size(float value, bool normalized, bool propagate);
    
    void init_fold(const std::string& name);
    void init_slider(NodeSliderH*& slider, const char* id, pp::app::BrushStrokeFloatSetting setting, float Brush::* prop);
    void handle_slide(pp::app::BrushStrokeFloatSetting setting, float Brush::* prop, Node* target, float value);

    void init_checkbox(NodeCheckBox*& slider, const char* id, pp::app::BrushStrokeBoolSetting setting, bool Brush::* prop);
    void handle_checkbox(pp::app::BrushStrokeBoolSetting setting, bool value);
    void execute_stroke_control_plan(const pp::app::BrushStrokeControlPlan& plan);
    void close_popup_overlay_handles() noexcept;
};
