#include "pch.h"

#include "canvas_modes.h"

#include "app.h"
#include "canvas.h"
#include "legacy_canvas_mode_helpers.h"
#include "legacy_ui_overlay_services.h"
#include "log.h"
#include "renderer_gl/opengl_capabilities.h"

using pp::legacy_canvas_mode::apply_canvas_mode_capability;
using pp::legacy_canvas_mode::query_canvas_mode_capability;

void CanvasModeMaskFree::init()
{
    m_shape.create();
}

void CanvasModeMaskFree::leave(kCanvasMode next)
{
//     Canvas::I->draw_objects(std::bind(&CanvasModeFill::on_Draw, this, glm::mat4(1), std::placeholders::_1, std::placeholders::_2));
    if (next != kCanvasMode::Draw && next != kCanvasMode::Erase && next != kCanvasMode::Line)
    {
        m_points.clear();
        Canvas::I->m_smask_active = false;
    }
}

void CanvasModeMaskFree::clear()
{
    m_points.clear();
    m_points2d.clear();
    m_shape.clear();
}

void CanvasModeMaskFree::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
{
    static glm::vec2 oldpos;
    static glm::vec2 oldvec;
    static float acc = 0.f;
    if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch)
        return;
    switch (me->m_type)
    {
    case kEventType::MouseDownL:
    {
        node->mouse_capture();
        m_dragging = true;
        m_points2d.clear();
        m_points.clear();
        oldpos = loc;
        oldvec = {1.f, 0.f};
        acc = 0;
        vertex_t vert;
        vert.pos = glm::vec4(loc, 0, 1);
        m_points2d.push_back(loc);
        m_points2d.push_back(loc);
        m_points.push_back(vert);
        m_points.push_back(vert);
        if (!(App::I->keys[(int)kKey::KeyShift] || App::I->keys[(int)kKey::KeyCtrl]))
            Canvas::I->m_smask.clear({0, 0, 0, 0});
        Canvas::I->m_smask_active = true;
        Canvas::I->m_smask_mode = 1;
        break;
    }
    case kEventType::MouseUpL:
        pp::panopainter::release_legacy_mouse_capture(*node);
        m_dragging = false;
        if (m_points2d.size() > 3)
        {
            if (!m_points.empty())
            {
                m_selection_cam = Canvas::I->get_camera();
                //m_points2d = poly_intersect(poly_remove_duplicate(m_points2d), Canvas::I->face_to_shape2D(0));
                auto drawer = [this](const glm::mat4& camera, const glm::mat4& proj) {
                    // blending state intentionally left unchanged here.
                    apply_canvas_mode_capability(pp::renderer::gl::depth_test_state(), false);
                    pp::panopainter::setup_legacy_vr_color_shader({
                        .color = App::I->keys[(int)kKey::KeyCtrl] ? glm::vec4(0, 0, 0, 1) : glm::vec4(1, 1, 1, 1),
                        .mvp = proj * camera,
                    });
                    m_shape.draw_fill();
                };
                // use m_shape to render the mask polygon
                auto v = triangulate(poly_remove_duplicate(m_points2d));
                Canvas::I->project2Dpoints(v);
                m_shape.update_vertices(v.data(), (int)v.size());
                Canvas::I->draw_objects_direct(std::bind(drawer, std::placeholders::_1, std::placeholders::_2), Canvas::I->m_smask, 0);

                // close the path and reset m_shape to contour rendering
                m_points.push_back(m_points.back());
                m_points.push_back(m_points.front());
                Canvas::I->project2Dpoints(m_points);
                m_shape.update_vertices(m_points.data(), (int)m_points.size());
            }
        }
        else
        {
            Canvas::I->m_smask_active = false;
        }
        break;
    case kEventType::MouseMove:
    {
        if (m_dragging)
        {
            auto v = loc - oldpos;
            float len = glm::length(v);
            if (len > 5)
            {
                m_points.back().pos = glm::vec4(loc, 0, 1);
                m_points2d.back() = loc;

                v = glm::normalize(v);
                float d = 1.f - glm::dot(v, oldvec);
                acc += d;
                oldpos = loc;
                oldvec = v;
                if (acc > 0.001) // angle change tolerance
                {
                    //LOG("d=%f acc=%f", d, acc);
                    acc = 0;

                    m_points2d.push_back(loc);
                    vertex_t vert;
                    vert.pos = glm::vec4(loc, 0, 1);
                    m_points.push_back(vert);
                    m_points.push_back(vert);
                }
                m_shape.update_vertices(m_points.data(), (int)m_points.size());
            }
        }
        break;
    }
    case kEventType::MouseCancel:
        if (m_dragging)
        {
            m_points.pop_back();
            m_shape.update_vertices(m_points.data(), (int)m_points.size());
        }
        m_dragging = false;
        pp::panopainter::release_legacy_mouse_capture(*node);
        if (m_points.size() < 4)
        {
            m_points.clear();
            m_shape.update_vertices(m_points.data(), (int)m_points.size());
        }
        break;
    default:
        break;
    }
}

void CanvasModeMaskFree::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera)
{
    const bool depth = query_canvas_mode_capability(pp::renderer::gl::depth_test_state());
    apply_canvas_mode_capability(pp::renderer::gl::depth_test_state(), false);
    if (m_points.size() > 3)
    {
        if (m_dragging)
        {
            pp::panopainter::setup_legacy_vr_color_shader({
                .color = { 0, 0, 0, 1 },
                .mvp = glm::scale(glm::vec3(1, -1, 1)) * ortho,
            });
            //m_dragging ? m_shape.draw_stroke() : m_shape.draw_fill();
            m_shape.draw_stroke();
        }
    }
    if (depth) apply_canvas_mode_capability(pp::renderer::gl::depth_test_state(), true);
}

void CanvasModeMaskLine::init()
{
    m_shape.create();
}

void CanvasModeMaskLine::leave(kCanvasMode next)
{
    if (next != kCanvasMode::Draw && next != kCanvasMode::Erase && next != kCanvasMode::Line)
    {
        m_points.clear();
        m_active_tool = false;
        Canvas::I->m_smask_active = false;
    }

    if (!m_active_tool)
        return;

    if (m_points2d.size() > 3)
    {
        std::vector<vertex_t> points;
        for (int i = 0; i < (int)m_points2d.size(); i++)
            points.emplace_back(m_points2d[i]);
        auto v = triangulate(poly_remove_duplicate(points));
        Canvas::I->project2Dpoints(v);
        LOG("%d points", (int)v.size());

        m_shape.update_vertices(v.data(), (int)v.size());

        if (!m_points.empty())
        {
            auto drawer = [this](const glm::mat4& camera, const glm::mat4& proj) {
                // blending state intentionally left unchanged here.
                pp::panopainter::setup_legacy_vr_color_shader({
                    .color = { 1, 1, 1, 1 },
                    .mvp = proj * camera,
                });
                m_shape.draw_fill();
            };
            Canvas::I->draw_objects_direct(std::bind(drawer, std::placeholders::_1, std::placeholders::_2), Canvas::I->m_smask, 0);

            //m_points.clear();
            // close the path
            m_points.push_back(m_points.back());
            m_points.push_back(m_points.back());
            m_points.push_back(m_points.front());
            Canvas::I->project2Dpoints(m_points);
            // reset m_shape to contour rendering
            m_shape.update_vertices(m_points.data(), (int)m_points.size());
        }
    }
    else
    {
        Canvas::I->m_smask_active = false;
    }
    m_active_tool = false;
}

void CanvasModeMaskLine::enter(kCanvasMode prev)
{
    //m_points2d.clear();
    //m_points.clear();
    //Canvas::I->m_smask.clear({0, 0, 0, 0});
    //m_active_tool = true;
}

void CanvasModeMaskLine::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
{
    if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch)
        return;
    switch (me->m_type)
    {
    case kEventType::MouseDownL:
    {
        if (m_active_tool == false)
        {
            m_active_tool = true;
            m_points2d.clear();
            m_points.clear();
            Canvas::I->m_smask.clear({ 0, 0, 0, 0 });
            Canvas::I->m_smask_active = true;
            Canvas::I->m_smask_mode = 2;
        }
        node->mouse_capture();
        m_dragging = true;
        m_points2d.push_back(loc);
        vertex_t vert;
        vert.pos = glm::vec4(loc, 0, 1);
        m_points.push_back(vert);
        m_shape.update_vertices(m_points.data(), (int)m_points.size());
        break;
    }
    case kEventType::MouseUpL:
        pp::panopainter::release_legacy_mouse_capture(*node);
        m_dragging = false;
        if (m_points.size() > 1)
        {
            m_selection_cam = Canvas::I->get_camera();
            m_points.push_back(m_points.back());
            m_shape.update_vertices(m_points.data(), (int)m_points.size());
        }
        break;
    case kEventType::MouseMove:
    {
        if (m_dragging)
        {
            if (glm::distance(m_points2d.front(), loc) < 10)
                loc = m_points2d.front();
            m_points.back().pos = glm::vec4(loc, 0, 1);
            m_points2d.back() = loc;
            m_shape.update_vertices(m_points.data(), (int)m_points.size());
        }
        break;
    }
    case kEventType::MouseCancel:
        if (m_dragging)
        {
            m_points.pop_back();
            m_shape.update_vertices(m_points.data(), (int)m_points.size());
        }
        m_dragging = false;
        pp::panopainter::release_legacy_mouse_capture(*node);
        if (m_points.size() < 4)
        {
            m_points.clear();
            m_shape.update_vertices(m_points.data(), (int)m_points.size());
        }
        break;
    default:
        break;
    }
}

void CanvasModeMaskLine::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera)
{
    if (m_points.size() > 1)
    {
        if (m_active_tool)
        {
            pp::panopainter::setup_legacy_vr_color_shader({
                .color = { 0, 0, 0, 1 },
                .mvp = glm::scale(glm::vec3(1, -1, 1)) * ortho,
            });
            //m_dragging ? m_shape.draw_stroke() : m_shape.draw_fill();
            m_shape.draw_stroke();
        }
        else
        {
            pp::panopainter::setup_legacy_vr_color_shader({
                .color = { 0, 0, 0, 1 },
                .mvp = proj * camera,
            });
            m_shape.draw_stroke();
        }
    }
}
