#include "pch.h"

#include "legacy_canvas_mode_helpers.h"

#include "app.h"
#include "canvas.h"
#include "canvas_modes.h"
#include "legacy_ui_overlay_services.h"
#include "legacy_ui_gl_dispatch.h"
#include "renderer_gl/opengl_capabilities.h"

NodeCanvas* CanvasMode::node;

namespace pp::legacy_canvas_mode {

void set_canvas_mode_active_texture_unit(std::uint32_t unit_index)
{
    pp::legacy::ui_gl::activate_texture_unit(unit_index, "CanvasMode");
}

void apply_canvas_mode_capability(std::uint32_t state, bool enabled)
{
    pp::legacy::ui_gl::set_capability(state, enabled, "CanvasMode");
}

bool query_canvas_mode_capability(std::uint32_t state)
{
    return pp::legacy::ui_gl::query_capability(state, "CanvasMode");
}

void apply_canvas_mode_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height)
{
    pp::legacy::ui_gl::apply_viewport(x, y, width, height, "CanvasMode");
}

std::uint32_t query_canvas_mode_read_framebuffer()
{
    return pp::legacy::ui_gl::query_read_framebuffer("CanvasMode");
}

void read_canvas_mode_pixel(std::int32_t x, std::int32_t y, glm::u8vec4& pixel)
{
    pp::legacy::ui_gl::read_framebuffer_rgba8_pixel(
        query_canvas_mode_read_framebuffer(),
        x,
        y,
        &pixel,
        "CanvasMode");
}

}

void CanvasModeBasicCamera::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
{
    switch (me->m_type)
    {
        case kEventType::MouseDownL:
//             if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch)
//             {
//                 m_draggingR = true;
//                 m_dragR_start = me->m_pos;
//                 m_pan_start = Canvas::I->m_pan;
//                 node->mouse_capture();
//             }
            if (App::I->keys[(int)kKey::KeySpacebar])
            {
                m_draggingL = true;
                m_pan_start = Canvas::I->m_pan;
                m_dragL_start = me->m_pos;
                node->mouse_capture();
            }
            break;
        case kEventType::MouseUpL:
//             if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch)
//             {
//                 m_draggingR = false;
//                 node->mouse_release();
//             }
            if (m_draggingL)
            {
                m_draggingL = false;
                pp::panopainter::release_legacy_mouse_capture(*node);
            }
            break;
        case kEventType::MouseDownR:
            if (App::I->keys[(int)kKey::KeyAlt])
                break;
            m_zooming = App::I->keys[(int)kKey::KeyCtrl];
            m_draggingR = true;
            m_dragR_start = me->m_pos;
            m_pan_start = Canvas::I->m_pan;
            m_fov_start = Canvas::I->m_cam_fov;
            node->mouse_capture();
            break;
        case kEventType::MouseUpR:
            m_draggingR = false;
            pp::panopainter::release_legacy_mouse_capture(*node);
            break;
        case kEventType::MouseMove:
            if (m_draggingR)
            {
                if (m_zooming)
                {
                    Canvas::I->m_cam_fov = glm::clamp(m_fov_start - (me->m_pos.x - m_dragR_start.x) * 0.05f,
                        Canvas::I->m_cam_fov_min, Canvas::I->m_cam_fov_max);
                }
                else
                {
                    const auto vr_session = App::I->vr_session_snapshot();
                    auto dir = (vr_session.has_vr && vr_session.vr_active) ? glm::vec2(1, 1) : glm::vec2(-1, -1);
                    Canvas::I->m_pan = m_pan_start + (me->m_pos - m_dragR_start) * dir * (Canvas::I->m_cam_fov / 85.f);
                    auto angle = Canvas::I->m_pan * 0.003f;
                    Canvas::I->m_cam_rot = glm::eulerAngleXY(angle.y, angle.x);
                }
            }
            if (m_draggingL)
            {
                Canvas::I->m_pan = m_pan_start + (me->m_pos - m_dragL_start) * glm::vec2(-1, -1) * (Canvas::I->m_cam_fov / 85.f);
                auto angle = Canvas::I->m_pan * 0.003f;
                Canvas::I->m_cam_rot = glm::eulerAngleXY(angle.y, angle.x);
            }
            break;
        case kEventType::MouseScroll:
            m_zoom_canvas += me->m_scroll_delta * 0.1f;
            Canvas::I->m_cam_fov = glm::clamp(Canvas::I->m_cam_fov - me->m_scroll_delta * 2.0f,
                Canvas::I->m_cam_fov_min, Canvas::I->m_cam_fov_max);
            //App::I->brush_update(true, true);
            break;
        case kEventType::MouseCancel:
            m_draggingR = false;
            pp::panopainter::release_legacy_mouse_capture(*node);
            break;
        default:
            break;
    }
}

void CanvasModeBasicCamera::on_GestureEvent(GestureEvent* ge)
{
    switch (ge->m_type)
    {
        case kEventType::GestureStart:
            m_pan_start = Canvas::I->m_pan;
            m_zoom_start = m_zoom_canvas;
            m_camera_fov = Canvas::I->m_cam_fov;
            break;
        case kEventType::GestureMove:
        {
            Canvas::I->m_pan = m_pan_start + ge->m_pos_delta * glm::vec2(-1, -1) * 0.3f * (Canvas::I->m_cam_fov / 85.f);
            Canvas::I->m_cam_fov = glm::clamp(m_camera_fov - ge->m_distance_delta * .05f,
                Canvas::I->m_cam_fov_min, Canvas::I->m_cam_fov_max);
            auto angle = Canvas::I->m_pan * 0.003f;
            Canvas::I->m_cam_rot = glm::eulerAngleXY(angle.y, angle.x);
            //App::I->brush_update(true, true);
            break;
        }
        default:
            break;
    }
}

void CanvasModeCamera::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::MouseDownR:
        Canvas::I->m_cam_pos = { 0, 0, 0 };
        break;
    case kEventType::MouseDownL:
        m_dragging = true;
        m_drag_start = me->m_pos;
        m_pos_start = xy(Canvas::I->m_cam_pos);
        node->mouse_capture();
        break;
    case kEventType::MouseUpL:
        m_dragging = false;
        pp::panopainter::release_legacy_mouse_capture(*node);
        Canvas::I->m_cam_pos = { 0, 0, 0 };
        break;
    case kEventType::MouseMove:
        if (m_dragging)
            Canvas::I->m_cam_pos = glm::vec3(m_pos_start + (me->m_pos - m_drag_start) * glm::vec2(1, -1) * 0.001f, Canvas::I->m_cam_pos.z);
        break;
    case kEventType::MouseCancel:
        m_dragging = false;
        pp::panopainter::release_legacy_mouse_capture(*node);
        break;
    default:
        break;
    }
}

void CanvasModeGrid::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:
        {
            node->mouse_capture();
            glm::vec3 ro, rd, hit_o, hit_d;
            glm::vec2 fb_pos;
            if (Canvas::I->point_trace(loc, ro, rd, hit_o, fb_pos, hit_d, m_plane_id))
            {
                m_action = std::make_unique<ActionModeGrid>();
                m_action->m_mode = this;
                m_action->m_highlight = m_highlight;
                m_action->m_selected_index = m_selected_index;
                m_action->m_lines = m_lines;

                int select = -1;
                for (int i = 0; i < m_lines.size(); i++)
                {
                    auto const& l = m_lines[i];
                    float d = lines_distance(ro, ro + rd * 10.f, l.o - l.d * 10.f, l.o + l.d * 10.f);
                    if (d < 0.03f)
                        select = i;
                }
                if (select == -1)
                {
                    m_lines.push_back({ hit_o, hit_d });
                    m_selected_index = (int)m_lines.size() - 1;
                    m_highlight = false;
                    m_added = true;
                }
                else
                {
                    m_selected_index = select;
                    m_highlight = true;
                    m_added = false;
                }
                origin = hit_o;
                dir = hit_d;
                m_dragging = true;
            }
            break;
        }
        case kEventType::MouseUpL:
            pp::panopainter::release_legacy_mouse_capture(*node);
            m_dragging = false;
            ActionManager::add(m_action.release());
            //commit();
            break;
        case kEventType::MouseMove:
        {
            glm::vec3 ro, rd, hit_o, hit_d;
            glm::vec2 hit_fb;
            if (m_dragging && Canvas::I->point_trace_plane(loc, ro, rd, hit_o, hit_d, hit_fb, m_plane_id))
            {
                m_lines[m_selected_index] = { hit_o, hit_d };
                origin = hit_o;
                dir = hit_d;
                m_dragging = true;
            }
            break;
        }
        case kEventType::MouseCancel:
            if (m_dragging && m_selected_index == m_lines.size() - 1)
                m_lines.pop_back();
            m_dragging = false;
            pp::panopainter::release_legacy_mouse_capture(*node);
            break;
        default:
            break;
    }
}

void CanvasModeGrid::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera)
{
    const glm::vec4 blue(0, 0, 1, 1);
    const glm::vec4 red(1, 0, 0, 1);
    for (int i = 0; i < m_lines.size(); i++)
    {
        auto const& l = m_lines[i];
        auto origin = l.o;
        auto dir = l.d;
        pp::panopainter::setup_legacy_vr_color_shader({
            .color = m_highlight && i == m_selected_index ? blue : red,
            .mvp = proj * camera,
        });
        static glm::vec4 AB[2];
        AB[0] = { origin - dir * 10.f, 1 };
        AB[1] = { origin + dir * 10.f, 1 };
        m_line.update_vertices(AB);
        m_line.draw_stroke();
    }
}

void CanvasModeGrid::init()
{
    m_line.create();
}

void CanvasModeGrid::commit()
{
    auto drawer = [this](const glm::mat4& camera, const glm::mat4& proj) {
        pp::panopainter::setup_legacy_vr_color_shader({
            .color = { 1, 0, 0, 1 },
            .mvp = proj * camera,
        });
        static glm::vec4 AB[2];
        AB[0] = { origin - dir * 10.f, 1 };
        AB[1] = { origin + dir * 10.f, 1 };
        m_line.update_vertices(AB);
        m_line.draw_stroke();
    };
    Canvas::I->draw_objects(std::bind(drawer, std::placeholders::_1, std::placeholders::_2), Canvas::I->layer().m_frame_index, true);
}

void CanvasModeGrid::clear()
{
    auto a = new ActionModeGrid;
    a->m_mode = this;
    a->m_highlight = m_highlight;
    a->m_selected_index = m_selected_index;
    a->m_lines = m_lines;
    ActionManager::add(a);

    m_lines.clear();
}

void CanvasModeGrid::leave(kCanvasMode next)
{
    m_selected_index = -1;
}

void CanvasModeGrid::on_KeyEvent(KeyEvent* ke)
{
    if ((ke->m_key == kKey::KeyBackspace || ke->m_key == kKey::KeyDel)
        && ke->m_type == kEventType::KeyUp)
    {
        if (m_highlight)
        {
            auto a = new ActionModeGrid;
            a->m_mode = this;
            a->m_highlight = m_highlight;
            a->m_selected_index = m_selected_index;
            a->m_lines = m_lines;
            ActionManager::add(a);
            m_lines.erase(m_lines.begin() + m_selected_index);
            m_highlight = false;
        }
    }
}

Action* ActionModeGrid::get_redo()
{
    auto a = new ActionModeGrid;
    a->m_mode = m_mode;
    a->m_highlight = m_mode->m_highlight;
    a->m_selected_index = m_mode->m_selected_index;
    a->m_lines = m_mode->m_lines;
    return a;
}

void ActionModeGrid::undo()
{
    m_mode->m_highlight = m_highlight;
    m_mode->m_selected_index = m_selected_index;
    m_mode->m_lines = m_lines;
}
