#pragma once
#include "event.h"
#include "shape.h"
#include "brush.h"
#include "texture.h"
#include "action.h"
#include <poly2tri.h>
#include "node_tool_bucket.h"

enum class kCanvasMode 
{ 
    Draw,
    Erase,
    Line,
    Camera,
    Grid,
    Import,
    Cut,
    Copy,
    Fill,
    MaskFree,
    MaskLine,
    FloodFill,
    COUNT,
};

struct CameraData
{
    glm::mat4 m_mv{ 1 };
    glm::mat4 m_proj{ 1 };
    glm::vec4 m_box{ 0 };
    glm::vec4 m_vp{ 0 };
    glm::vec2 m_pan{ 0 };
    glm::mat4 m_plane_unproject[6] = SIXPLETTE(glm::mat4(1));
    glm::vec3 m_plane_dir[6] = SIXPLETTE(glm::vec3(0));
};

class CanvasMode
{
public:
    static class NodeCanvas* node;
    bool hide_curor = false;
    bool m_draw_tip = false;
    //static Canvas* canvas;
    virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) {}
    virtual void on_KeyEvent(KeyEvent* ke) {}
    virtual void on_GestureEvent(GestureEvent* ge) {}
    virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) {}
    virtual void init() {}
    virtual void enter(kCanvasMode prev) {}
    virtual void leave(kCanvasMode next) {}
    CanvasMode() = default;
    virtual ~CanvasMode() = default;
};

class CanvasModeBasicCamera : public CanvasMode
{
    bool m_draggingR = false;
    bool m_draggingL = false;
    bool m_draggingT = false; // touch drag
    bool m_zooming = false;
    glm::vec2 m_dragR_start;
    glm::vec2 m_dragL_start;
    glm::vec2 m_pan_start;
    float m_fov_start;
    float m_camera_fov;
    float m_zoom_canvas = 1.f;
    float m_zoom_start;
public:
    virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
    virtual void on_GestureEvent(GestureEvent* ge) override;
};

class CanvasModePen : public CanvasMode
{
    friend class App;
    bool m_dragging = false;
    glm::vec2 m_pan_start;
    glm::vec2 m_size_pos_start;
    float m_size_value_start;
    float m_camera_fov;
    float m_zoom_canvas = 1.f;
    float m_zoom_start;
    // resizing the tip
public:
    CanvasModePen() { hide_curor = true; m_picking = false; }
    
    virtual void on_GestureEvent(GestureEvent* ge) override;
    virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
    virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override;
    virtual void enter(kCanvasMode prev) override;
    virtual void leave(kCanvasMode next) override;
    bool m_resizing = false;
    bool m_picking = false;
    bool m_drawing = false;
    glm::vec2 m_cur_pos;
    bool m_draw_outline = true;
};

class CanvasModeLine : public CanvasMode
{
    LineSegment m_line;
    bool m_dragging = false;
    glm::vec2 m_drag_start;
    glm::vec2 m_drag_pos;
public:
    glm::vec2 m_cur_pos;
    virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
    virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override;
    virtual void init() override;
    virtual void enter(kCanvasMode prev) override;
    virtual void leave(kCanvasMode next) override;
};

class CanvasModeFloodFill : public CanvasMode
{
    const std::string m_cursor_path = "data/cursor/bucket-fill.png";
    const uint16_t m_cursor_id = const_hash(m_cursor_path.c_str());
    glm::vec2 m_cur_pos;
    NodeToolBucket* m_tool;
public:
    CanvasModeFloodFill() { hide_curor = true; }
    virtual void init() override;
    virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
    virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override;
    virtual void enter(kCanvasMode prev) override;
    virtual void leave(kCanvasMode next) override;
};


struct ray_t { glm::vec3 o, d; };
class CanvasModeGrid : public CanvasMode
{
    LineSegment m_line;
    glm::vec3 origin;
    glm::vec3 dir;
    int m_plane_id;
    bool m_dragging = false;
    bool m_added = false;
    std::unique_ptr<class ActionModeGrid> m_action;
public:
    bool m_highlight = false;
    int m_selected_index = -1;
    std::vector<ray_t> m_lines;
    virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
    virtual void on_KeyEvent(KeyEvent* ke) override;
    virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override;
    virtual void init() override;
    virtual void leave(kCanvasMode next) override;
    void commit();
    void clear();
};
struct ActionModeGrid : public Action
{
    CanvasModeGrid* m_mode;
    bool m_highlight;
    int m_selected_index;
    std::vector<ray_t> m_lines;
    virtual void run() override { }
    virtual Action* get_redo() override;
    virtual void undo() override;
    virtual size_t memory() override { return 0; }
};

class CanvasModeCamera : public CanvasMode
{
    bool m_dragging = false;
    glm::vec2 m_drag_start;
    glm::vec2 m_pos_start;
public:
    virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
};

class CanvasModeFill : public CanvasMode
{
    DynamicShape m_shape;
    bool m_dragging = false;
    std::vector<vertex_t> m_points;
    std::map<int, int> m_dirty_planes;
public:
    virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override;
    virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
    virtual void init() override;
    virtual void leave(kCanvasMode next) override;
};

class CanvasModeMaskBase : public CanvasMode
{
protected:
    friend class CanvasModeTransform;
    DynamicShape m_shape;
    bool m_dragging = false;
    std::vector<vertex_t> m_points;
    std::vector<glm::vec2> m_points2d;
    CameraData m_selection_cam;
public:
};

class CanvasModeMaskFree : public CanvasModeMaskBase
{
public:
    virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override;
    virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
    virtual void init() override;
    virtual void leave(kCanvasMode next) override;
    void clear();
};

class CanvasModeMaskLine : public CanvasModeMaskBase
{
    std::map<int, int> m_dirty_planes;
    bool m_active_tool = false;
public:
    virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override;
    virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
    virtual void init() override;
    virtual void enter(kCanvasMode prev) override;
    virtual void leave(kCanvasMode next) override;
};

class CanvasModeTransform : public CanvasMode
{
    DynamicShape m_shape[6]{};
    Sphere m_sphere{};
    Circle m_circle{};
    Texture2D m_tex[6]{};
    glm::vec3 m_origin{0, 0, 1 };
    std::vector<glm::vec3> corners{};
    std::vector<glm::vec2> corners2d{};
    int corner_hl = -1;
    glm::vec3 m_center_point{};
    glm::vec2 m_center_point_uv{};
    glm::mat4 m_xform{1};
    glm::mat4 m_xform_local{1};
    int m_counter = 0;
    bool m_dragging = false;
    int m_drag_corner = -1;
    float m_drag_diag = 0;
    std::vector<glm::vec2> m_drag_corners2d{};
    glm::vec2 m_drag_start{};
    glm::mat4 m_drag_xform{1};
    glm::mat4 m_drag_xform_local{1};
    std::vector<vertex_t> m_points{};
    std::vector<glm::vec2> m_points2d{};
    std::vector<vertex_t> m_points_face[6];
    std::map<int, int> m_dirty_planes{};
    bool m_active_tool = false;
    bool m_commit_on_leave = false;
public:
    enum class ActionType : uint8_t { Copy, Cut, Import };
    ActionType m_action;
    Image m_source_image;
    CanvasModeTransform() = default;
    virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override;
    virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
    virtual void init() override;;
    virtual void enter(kCanvasMode prev) override;
    virtual void leave(kCanvasMode next) override;
};
