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

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 buffer_target = pp::renderer::gl::array_buffer_target();
        const auto upload_usage = pp::renderer::gl::static_draw_buffer_usage();
        const auto attribute_type = pp::renderer::gl::vertex_attribute_float_component_type();
        const auto attribute_normalized =
            static_cast<GLboolean>(pp::renderer::gl::vertex_attribute_not_normalized());

        glGenBuffers(1, &buffers);
        glBindBuffer(buffer_target, buffers);
        glBufferData(buffer_target, vertices.size() * sizeof(vertex_t), vertices.data(), upload_usage);
        glBindBuffer(buffer_target, 0);

        glGenVertexArrays(1, &arrays);
        glBindVertexArray(arrays);
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);
        glEnableVertexAttribArray(2);
        glBindBuffer(buffer_target, buffers);
        glVertexAttribPointer(
            0,
            4,
            attribute_type,
            attribute_normalized,
            sizeof(vertex_t),
            (GLvoid*)0);
        glVertexAttribPointer(
            1,
            2,
            attribute_type,
            attribute_normalized,
            sizeof(vertex_t),
            (GLvoid*)offsetof(vertex_t, uvs));
        glVertexAttribPointer(
            2,
            4,
            attribute_type,
            attribute_normalized,
            sizeof(vertex_t),
            (GLvoid*)offsetof(vertex_t, col));
        glBindVertexArray(0);
    });
}

void NodeColorWheel::draw()
{
    pp::legacy::ui_gl::set_blend_enabled(false, "NodeColorWheel");
    ShaderManager::use(kShader::ColorHue);
    ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp * glm::eulerAngleZ(glm::radians(-90.f)));
    ShaderManager::u_int(kShaderUniform::Direction, 0); // set horizontal
    m_circle.draw_fill();

//    ShaderManager::use(kShader::ColorTri);
//    ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp);
//    ShaderManager::u_vec4(kShaderUniform::Col, glm::vec4(m_hsv, 0.f));
//    glBindVertexArray(arrays);
//    glDrawArrays(pp::renderer::gl::primitive_mode_for_fill_count(3U), 0, 3);
//    glBindVertexArray(0);

    ShaderManager::use(kShader::Color);
    ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp * glm::eulerAngleZ(glm::radians(-360.f * m_hsv.x)) * glm::translate(glm::vec3(.45f,0.f,0.f)));
    ShaderManager::u_vec4(kShaderUniform::Col, {convert_hsv2rgb({glm::fract(m_hsv.x + 0.5f), 1.f, 1.f}), 1.f});
    m_cur_hue.draw_stroke();
    
    ShaderManager::use(kShader::ColorQuad);
    ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp);
    ShaderManager::u_vec4(kShaderUniform::Col, 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);
    ShaderManager::use(kShader::Color);
    ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp * glm::translate(pos * quad_scale));
    ShaderManager::u_vec4(kShaderUniform::Col, {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:
        mouse_release();
        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:
        mouse_release();
        dragging = false;
        m_hsv = m_old_value;
        handle_color_change();
        break;
    default:
        return kEventResult::Available;
        break;
    }
    return kEventResult::Consumed;
}
