#include "pch.h"

#include "legacy_node_canvas_state_services.h"

#include "app.h"
#include "app_core/canvas_tool_ui.h"
#include "app_core/canvas_view.h"
#include "log.h"
#include "renderer_gl/opengl_capabilities.h"

namespace pp::panopainter {
namespace {

pp::app::CanvasToolMode canvas_tool_mode(kCanvasMode mode) noexcept
{
    switch (mode) {
    case kCanvasMode::Draw:
        return pp::app::CanvasToolMode::draw;
    case kCanvasMode::Erase:
        return pp::app::CanvasToolMode::erase;
    case kCanvasMode::Line:
        return pp::app::CanvasToolMode::line;
    case kCanvasMode::Camera:
        return pp::app::CanvasToolMode::camera;
    case kCanvasMode::Grid:
        return pp::app::CanvasToolMode::grid;
    case kCanvasMode::Copy:
        return pp::app::CanvasToolMode::copy;
    case kCanvasMode::Cut:
        return pp::app::CanvasToolMode::cut;
    case kCanvasMode::Fill:
        return pp::app::CanvasToolMode::fill;
    case kCanvasMode::MaskFree:
        return pp::app::CanvasToolMode::mask_free;
    case kCanvasMode::MaskLine:
        return pp::app::CanvasToolMode::mask_line;
    case kCanvasMode::FloodFill:
        return pp::app::CanvasToolMode::flood_fill;
    case kCanvasMode::COUNT:
        break;
    }
    return pp::app::CanvasToolMode::draw;
}

pp::app::CanvasCursorVisibilityMode canvas_cursor_visibility_mode(NodeCanvas::kCursorVisibility mode) noexcept
{
    switch (mode) {
    case NodeCanvas::kCursorVisibility::Never:
        return pp::app::CanvasCursorVisibilityMode::never;
    case NodeCanvas::kCursorVisibility::SmallBrush:
        return pp::app::CanvasCursorVisibilityMode::small_brush;
    case NodeCanvas::kCursorVisibility::NotPainting:
        return pp::app::CanvasCursorVisibilityMode::not_painting;
    case NodeCanvas::kCursorVisibility::Always:
        return pp::app::CanvasCursorVisibilityMode::always;
    }
    return pp::app::CanvasCursorVisibilityMode::never;
}

} // namespace

void restore_legacy_node_canvas_context(NodeCanvas& node_canvas)
{
    static_cast<Node&>(node_canvas).restore_context();
    const int canvas_resolution = App::I->default_canvas_resolution();
    node_canvas.m_canvas->create(canvas_resolution, canvas_resolution);

    node_canvas.m_sampler.create();

    node_canvas.m_sampler.set_filter(
        pp::renderer::gl::linear_texture_filter(),
        pp::renderer::gl::nearest_texture_filter());
    node_canvas.m_face_plane.create<1>(2, 2);
    node_canvas.m_canvas->snapshot_restore();
    CanvasMode::node = &node_canvas;
    for (int i = 0; i < (int)kCanvasMode::COUNT; i++)
        for (auto m : Canvas::modes[i])
            m->init();
}

void clear_legacy_node_canvas_context(NodeCanvas& node_canvas)
{
    static_cast<Node&>(node_canvas).clear_context();
    node_canvas.m_canvas->snapshot_save();
    node_canvas.m_canvas->clear_context();
    // TODO: clear CanvasMode objects
}

void handle_legacy_node_canvas_resize(NodeCanvas& node_canvas, glm::vec2 old_size, glm::vec2 new_size, float zoom)
{
    (void)old_size;
    (void)zoom;
    if (new_size.x != node_canvas.m_canvas->m_width || new_size.y != node_canvas.m_canvas->m_height)
    {
        new_size = new_size * node_canvas.m_density;
        create_legacy_node_canvas_buffers(node_canvas);
    }
}

void reset_legacy_node_canvas_camera(NodeCanvas& node_canvas)
{
    const auto state = pp::app::plan_canvas_camera_reset();
    node_canvas.m_canvas->m_cam_rot = glm::mat4(
        state.rotation[0], state.rotation[1], state.rotation[2], state.rotation[3],
        state.rotation[4], state.rotation[5], state.rotation[6], state.rotation[7],
        state.rotation[8], state.rotation[9], state.rotation[10], state.rotation[11],
        state.rotation[12], state.rotation[13], state.rotation[14], state.rotation[15]);
    node_canvas.m_canvas->m_cam_pos = {
        state.position[0],
        state.position[1],
        state.position[2],
    };
    node_canvas.m_canvas->m_cam_fov = state.field_of_view_degrees;
    node_canvas.m_canvas->m_pan = {
        state.pan[0],
        state.pan[1],
    };
}

void create_legacy_node_canvas_buffers(NodeCanvas& node_canvas)
{
    auto new_size = node_canvas.GetSize() * node_canvas.m_density;
    LOG("NodeCanvas::create_buffers size: %d x %d density %f", (int)new_size.x, (int)new_size.y, node_canvas.m_density);
    node_canvas.m_canvas->m_mixer.create((int)new_size.x * node_canvas.m_canvas->m_mixer_scale,
        (int)new_size.y * node_canvas.m_canvas->m_mixer_scale, -1, pp::renderer::gl::rgba8_internal_format());
    node_canvas.m_blender_rtt.create((int)new_size.x, (int)new_size.y, -1, pp::renderer::gl::rgba8_internal_format());
    node_canvas.m_cache_rtt.create((int)new_size.x, (int)new_size.y, -1, pp::renderer::gl::rgba8_internal_format());
    node_canvas.m_rtt.create((int)new_size.x, (int)new_size.y, -1, pp::renderer::gl::rgba8_internal_format(), true);
    node_canvas.m_blender_bg.create((int)new_size.x, (int)new_size.y, pp::renderer::gl::rgba8_internal_format());
}

void set_legacy_node_canvas_density(NodeCanvas& node_canvas, float density)
{
    node_canvas.m_density = density;
    create_legacy_node_canvas_buffers(node_canvas);
}

void set_legacy_node_canvas_cursor_visibility(NodeCanvas& node_canvas, NodeCanvas::kCursorVisibility mode)
{
    node_canvas.m_cursor_visibility = mode;
}

void update_legacy_node_canvas_cursor(NodeCanvas& node_canvas)
{
    auto* pen_mode = node_canvas.m_canvas->get_mode<CanvasModePen>();
    const auto plan = pp::app::plan_canvas_cursor_visibility(pp::app::CanvasCursorVisibilityInput {
        .mode = canvas_tool_mode(node_canvas.m_canvas->m_current_mode),
        .visibility_mode = canvas_cursor_visibility_mode(node_canvas.m_cursor_visibility),
        .has_current_brush = node_canvas.m_canvas->m_current_brush != nullptr,
        .brush_tip_size = node_canvas.m_canvas->m_current_brush ? node_canvas.m_canvas->m_current_brush->m_tip_size : 0.0F,
        .pen_is_drawing = pen_mode && pen_mode->m_drawing,
        .alt_down = App::I && App::I->keys[(int)kKey::KeyAlt],
        .pen_is_resizing = pen_mode && pen_mode->m_resizing,
        .pen_is_picking = pen_mode && pen_mode->m_picking,
    });
    if (!plan) {
        LOG("Canvas cursor visibility planning failed: %s", plan.status().message);
        App::I->show_cursor();
        return;
    }

    plan.value().visible ? App::I->show_cursor() : App::I->hide_cursor();
}

void tick_legacy_node_canvas_state(NodeCanvas& node_canvas, float dt)
{
    node_canvas.m_outline_pan = glm::fract(node_canvas.m_outline_pan + dt * 0.01f);
}

void destroy_legacy_node_canvas_state(NodeCanvas& node_canvas)
{
    node_canvas.m_blender_rtt.destroy();
    node_canvas.m_cache_rtt.destroy();
    node_canvas.m_rtt.destroy();
    node_canvas.m_blender_bg.destroy();
    node_canvas.m_face_plane.destroy();
    node_canvas.m_line.destroy();
    node_canvas.m_grid.destroy();
    static_cast<Node&>(node_canvas).destroy();
}

} // namespace pp::panopainter
