#include "pch.h"
#include "legacy_gl_mesh_dispatch.h"
#include "legacy_ui_gl_dispatch.h"
#include "legacy_ui_overlay_services.h"
#include "node_colorwheel.h"
#include "renderer_gl/opengl_capabilities.h"
#include "shader.h"
#include "log.h"
#include "app.h"

#include <array>
#include <cstddef>
#include <cstdint>

Node* NodeColorWheel::clone_instantiate() const
{
    return new NodeColorWheel;
}

void NodeColorWheel::clone_finalize(Node* dest) const
{
    NodeColorWheel* n = (NodeColorWheel*)dest;
    n->init_controls();
    n->m_mouse_ignore = false;
}

void NodeColorWheel::init()
{
    m_mouse_ignore = false;
    //init_template("color-picker");
    init_controls();
}

void NodeColorWheel::init_controls()
{
}

void NodeColorWheel::loaded()
{
    m_circle.create<64>(.5f, .4f, Circle::kUVMapping::Tube);
    m_cur_hue.create<16>(.05f, 0.04f);
    m_cur_quad.create<16>(.04f, 0.03f, Circle::kUVMapping::Tube);

    float quad_scale = glm::sin(glm::radians(45.f)) * 0.8f;
    m_quad.create<1>(quad_scale, quad_scale);
    
    struct vertex_t { glm::vec4 pos; glm::vec2 uvs; glm::vec4 col; };
    std::vector<vertex_t> vertices;
    float l = 0.4f;
    vertices.push_back({{glm::cos(4.f/3.f*glm::pi<float>())*l,glm::sin(4.f/3.f*glm::pi<float>())*l,0,1},{1,-1},{1,1,1,1}});
    vertices.push_back({{glm::cos(2.f/3.f*glm::pi<float>())*l,glm::sin(2.f/3.f*glm::pi<float>())*l,0,1},{0,0},{0,0,0,1}});
    vertices.push_back({{l,0,0,1},{1,1},{1,0,0,1}});

    App::I->render_task([&]
    {
        const auto attribute_type = pp::renderer::gl::vertex_attribute_float_component_type();
        const auto attribute_normalized =
            static_cast<std::uint8_t>(pp::renderer::gl::vertex_attribute_not_normalized());
        const std::array<pp::renderer::gl::OpenGlVertexAttribute, 3> attributes {
            pp::renderer::gl::OpenGlVertexAttribute {
                .index = 0U,
                .component_count = 4,
                .component_type = attribute_type,
                .normalized = attribute_normalized,
                .stride = static_cast<std::int32_t>(sizeof(vertex_t)),
                .offset = 0U,
            },
            pp::renderer::gl::OpenGlVertexAttribute {
                .index = 1U,
                .component_count = 2,
                .component_type = attribute_type,
                .normalized = attribute_normalized,
                .stride = static_cast<std::int32_t>(sizeof(vertex_t)),
                .offset = static_cast<std::uintptr_t>(offsetof(vertex_t, uvs)),
            },
            pp::renderer::gl::OpenGlVertexAttribute {
                .index = 2U,
                .component_count = 4,
                .component_type = attribute_type,
                .normalized = attribute_normalized,
                .stride = static_cast<std::int32_t>(sizeof(vertex_t)),
                .offset = static_cast<std::uintptr_t>(offsetof(vertex_t, col)),
            },
        };

        const auto mesh = pp::legacy::gl_mesh::create_mesh_objects(
            pp::renderer::gl::OpenGlMeshUpload {
                .vertex_data = vertices.data(),
                .vertex_byte_count = static_cast<std::intptr_t>(vertices.size() * sizeof(vertex_t)),
                .indexed = false,
                .vertex_array_count = 1U,
                .attributes = attributes,
            },
            "NodeColorWheel::loaded");
        buffers = static_cast<GLuint>(mesh.vertex_buffer);
        arrays = static_cast<GLuint>(mesh.vertex_arrays[0]);
    });
}

void NodeColorWheel::draw()
{
    pp::legacy::ui_gl::set_blend_enabled(false, "NodeColorWheel");
    pp::panopainter::configure_legacy_slider_hue_shader(
        m_mvp * glm::eulerAngleZ(glm::radians(-90.f)),
        0); // set horizontal
    m_circle.draw_fill();

    pp::panopainter::configure_legacy_ui_color_shader(
        m_mvp * glm::eulerAngleZ(glm::radians(-360.f * m_hsv.x)) * glm::translate(glm::vec3(.45f,0.f,0.f)),
        {convert_hsv2rgb({glm::fract(m_hsv.x + 0.5f), 1.f, 1.f}), 1.f});
    m_cur_hue.draw_stroke();
    
    pp::panopainter::configure_legacy_ui_color_quad_shader(m_mvp, glm::vec4(m_hsv, 0.f));
    m_quad.draw_fill();

    float quad_scale = glm::sin(glm::radians(45.f)) * 0.8f;
    glm::vec3 pos = glm::vec3(glm::vec2(m_hsv.y, 1.f - m_hsv.z) - 0.5f, 0);
    pp::panopainter::configure_legacy_ui_color_shader(
        m_mvp * glm::translate(pos * quad_scale),
        {convert_hsv2rgb({glm::fract(m_hsv.x + 0.5f), 1.f, 1.f}), 1.f});
    m_cur_quad.draw_fill();
}

glm::vec4 NodeColorWheel::get_quad_rect() const
{
    float quad_scale = glm::sin(glm::radians(45.f)) * 0.8f;
    glm::vec2 size = m_size * quad_scale;
    glm::vec2 pos = (m_size - size) * 0.5f;
    return glm::vec4(pos, size);
}

bool NodeColorWheel::inside_quad(glm::vec2 pos, glm::vec2& out_coord) const
{
    auto r = get_quad_rect();
    out_coord = (pos - xy(r)) / zw(r);
    return point_in_rect(pos, r);
}

void NodeColorWheel::handle_color_change()
{
    if (on_value_changed)
        on_value_changed(this, m_hsv);
}

kEventResult NodeColorWheel::handle_event(Event* e)
{
    Node::handle_event(e);
    auto* me = static_cast<MouseEvent*>(e);
    switch (e->m_type)
    {
    case kEventType::MouseDownL:
    {
        m_old_value = m_hsv;
        dragging = true;
        mouse_capture();
        
        auto pos = (me->m_pos - m_pos - GetSize() * 0.5f) / GetSize();
        float l = glm::length(pos);

        glm::vec2 quad_pos(0);
        if (inside_quad(me->m_pos - m_pos, quad_pos))
        {
            mode = 2;
            m_hsv.y = glm::clamp(quad_pos.x, 0.f, 1.f);
            m_hsv.z = 1.f - glm::clamp(quad_pos.y, 0.f, 1.f);
            handle_color_change();
        }
        else if (l >= 0.4f && l <= 0.5f)
        {
            mode = 1;
            auto normalized_pos = glm::normalize(me->m_pos - m_pos - GetSize() * 0.5f);
            m_hsv.x = (glm::atan(normalized_pos.y, -normalized_pos.x) + glm::pi<float>()) / glm::two_pi<float>();
            handle_color_change();
        }
        else
        {
            mode = 0;
        }
    }
    break;
    case kEventType::MouseUpL:
        pp::panopainter::release_legacy_mouse_capture(*this);
        dragging = false;
        break;
    case kEventType::MouseMove:
        if (dragging)
        {
            if (mode == 1)
            {
                auto pos = glm::normalize(me->m_pos - m_pos - GetSize() * 0.5f);
                m_hsv.x = (glm::atan(pos.y, -pos.x) + glm::pi<float>()) / glm::two_pi<float>();
                handle_color_change();
            }
            else if (mode == 2)
            {
                glm::vec2 quad_pos(0);
                inside_quad(me->m_pos - m_pos, quad_pos);
                m_hsv.y = glm::clamp(quad_pos.x, 0.f, 1.f);
                m_hsv.z = 1.f - glm::clamp(quad_pos.y, 0.f, 1.f);
                handle_color_change();
            }
        }
        break;
    case kEventType::MouseCancel:
        pp::panopainter::release_legacy_mouse_capture(*this);
        dragging = false;
        m_hsv = m_old_value;
        handle_color_change();
        break;
    default:
        return kEventResult::Available;
        break;
    }
    return kEventResult::Consumed;
}
