#include "pch.h"
#include "log.h"
#include "node_panel_layer.h"
#include "canvas.h"
#include "node_combobox.h"

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

void NodeLayer::clone_children(Node* dest) const
{
    NodeBorder::clone_children(dest);
    NodeLayer* n = static_cast<NodeLayer*>(dest);
    n->m_label = n->find<NodeText>("label");
    n->m_visibility = n->find<NodeCheckBox>("cb");
}

void NodeLayer::clone_copy(Node* dest) const
{
    NodeBorder::clone_copy(dest);
    NodeLayer* n = (NodeLayer*)dest;
    n->m_selected = m_selected;
    n->m_label_text = m_label_text;
}

void NodeLayer::init()
{
    const auto& m_template = (NodeBorder*)init_template("tpl-layer");
    m_color = m_template->m_color;
    m_border_color = m_template->m_border_color;
    m_thinkness = m_template->m_thinkness;
    m_label = find<NodeText>("label");
    m_visibility = find<NodeCheckBox>("cb");
}

void NodeLayer::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
{
    NodeBorder::parse_attributes(ka, attr);
    switch (ka)
    {
    case kAttribute::Text:
        m_label_text = attr->Value();
        break;
    case kAttribute::Selected:
        m_selected = attr->BoolValue();
    default:
        break;
    }
}

void NodeLayer::loaded()
{
    NodeBorder::loaded();
    if (!m_label_text.empty())
        m_label->set_text(m_label_text.c_str());
    m_visibility->on_value_changed = [this](Node*, bool checked) {
        if (on_visibility_changed)
            on_visibility_changed(this, checked);
    };
}

kEventResult NodeLayer::handle_event(Event* e)
{
    NodeBorder::handle_event(e);
    switch (e->m_type)
    {
    case kEventType::MouseEnter:
        break;
    case kEventType::MouseLeave:
        break;
    case kEventType::MouseDownL:
        m_selected = true;
        if (on_selected)
            on_selected(this);
        if (on_highlight)
            on_highlight(this, true);
        mouse_capture();
        break;
    case kEventType::MouseUpL:
        if (on_highlight)
            on_highlight(this, false);
        mouse_release();
        break;
    default:
        return kEventResult::Available;
        break;
    }
    return kEventResult::Consumed;
}

void NodeLayer::draw()
{
    auto c = m_selected ? m_color_selected : m_color_normal;
    m_thinkness = m_selected ? 1.f : 0.f;
    m_color = m_mouse_inside ? m_color_hover : c;
    NodeBorder::draw();
}

void NodeLayer::set_name(const char* s)
{
    m_label_text = s;
    m_label->set_text(s);
}
Node* NodePanelLayer::clone_instantiate() const
{
    return new NodePanelLayer();
}

void NodePanelLayer::init()
{
    init_template("tpl-panel-layers");
    m_layers_container = find<NodeBorder>("layers-container");
    btn_add = find<NodeButtonCustom>("btn-add");
    btn_remove = find<NodeButtonCustom>("btn-remove");
    btn_up = find<NodeButtonCustom>("btn-up");
    btn_down = find<NodeButtonCustom>("btn-down");
    btn_duplicate = find<NodeButtonCustom>("btn-duplicate");
    btn_duplicate->on_click = [this](Node*) {
        std::string next = m_current_layer->m_label_text + "01";
        std::regex r(R"(([^\d]*)(\d+)$)");
        std::smatch m;
        if (std::regex_search(m_current_layer->m_label_text, m, r))
        {
            auto num = m[2].str();
            int count = atoi(num.c_str()) + 1;
            char tmp[128];
            sprintf(tmp, "%s%0*d", m[1].str().c_str(), num.length(), count);
            next = tmp;
        }
        int source_index = m_layers_container->get_child_index(m_current_layer);
        add_layer(next.c_str());
        if (on_layer_duplicate)
            on_layer_duplicate(this, source_index);
        if (on_layer_change)
            on_layer_change(this, -1, m_layers_container->get_child_index(m_current_layer));
        update_attributes();
    };
    btn_add->on_click = [this](Node*) {
        add_layer();
        if (on_layer_add)
            on_layer_add(this);
        if (on_layer_change)
            on_layer_change(this, -1, m_layers_container->get_child_index(m_current_layer));
        update_attributes();
    };
    btn_remove->on_click = [this](Node*) {
        if (m_layers.size() == 1)
            return; // dont' delete the last layer
        remove_layer(m_current_layer);
    };
    btn_up->on_click = [this](Node*) {
        int old_idx = m_layers_container->get_child_index(m_current_layer);
        m_layers_container->move_child_offset(m_current_layer, -1);
        int new_idx = m_layers_container->get_child_index(m_current_layer);
        if (on_layer_order && old_idx != new_idx)
        {
            on_layer_order(this, old_idx, new_idx);
        }
    };
    btn_down->on_click = [this](Node*) {
        int old_idx = m_layers_container->get_child_index(m_current_layer);
        m_layers_container->move_child_offset(m_current_layer, +1);
        int new_idx = m_layers_container->get_child_index(m_current_layer);
        if (on_layer_order && old_idx != new_idx)
        {
            on_layer_order(this, old_idx, new_idx);
        }
    };
    m_opacity = find<NodeSliderH>("opacity");
    m_opacity->on_value_changed = [this](Node*, float value) {
        handle_layer_opacity(m_current_layer, value);
    };
    m_alpha_lock = find<NodeCheckBox>("alpha-lock");
    m_alpha_lock->on_value_changed = [this](Node*, bool locked) {
        handle_layer_alpha_lock(m_current_layer, locked);
    };
    m_blend_mode = find<NodeComboBox>("blend-mode");
    m_blend_mode->on_select = [this](Node*, int index) {
        handle_layer_blend_mode(m_current_layer, index);
    };
}

NodeLayer* NodePanelLayer::add_layer(const char* name)
{
    NodeLayer* l = new NodeLayer;
    m_layers_container->add_child(l);
    l->init();
    l->create();
    l->loaded();
    l->set_name(name);
    l->m_visibility->set_value(true);
    l->on_selected = std::bind(&NodePanelLayer::handle_layer_selected, this, std::placeholders::_1);
    l->on_visibility_changed = std::bind(&NodePanelLayer::handle_layer_visibility, this, std::placeholders::_1, std::placeholders::_2);
    l->on_highlight = std::bind(&NodePanelLayer::handle_layer_highlight, this, std::placeholders::_1, std::placeholders::_2);
    if (m_current_layer)
        m_current_layer->m_selected = false;
    m_current_layer = l;
    m_current_layer->m_selected = true;
    m_layers.push_back(l);
    update_attributes();
    return l;
}

void NodePanelLayer::add_layer()
{
    static char s[64];
    sprintf(s, "Layer-%d", id_counter++);
    add_layer(s);
}

NodeLayer* NodePanelLayer::get_layer_at(int index)
{
    return static_cast<NodeLayer*>(m_layers_container->get_child_at(index));
}

void NodePanelLayer::remove_layer(NodeLayer* layer)
{
    auto it = std::find(m_layers.begin(), m_layers.end(), m_current_layer);
    auto i = m_layers_container->get_child_index(m_current_layer);
    int old_idx = i;// (int)std::distance(m_layers.begin(), it);
    m_layers_container->remove_child(m_current_layer);
    m_layers.erase(it);
    i = std::min<int>(i, (int)m_layers.size() - 1);
    m_current_layer = (NodeLayer*)m_layers_container->get_child_at(i);
    m_current_layer->m_selected = true;
    if (on_layer_delete)
        on_layer_delete(this, old_idx);
    if (on_layer_change)
        on_layer_change(this, -1, i);
    update_attributes();
}

void NodePanelLayer::handle_layer_opacity(NodeLayer* target, float value)
{
    if (on_layer_opacity_changed)
        on_layer_opacity_changed(this, m_layers_container->get_child_index(target), value);
}

void NodePanelLayer::handle_layer_highlight(NodeLayer* target, bool highlight)
{
    if (on_layer_highlight_changed)
        on_layer_highlight_changed(this, m_layers_container->get_child_index(target), highlight);
}

void NodePanelLayer::handle_layer_visibility(NodeLayer* target, bool visible)
{
    if (on_layer_visibility_changed)
        on_layer_visibility_changed(this, m_layers_container->get_child_index(target), visible);
}

void NodePanelLayer::handle_layer_alpha_lock(NodeLayer* target, bool locked)
{
    if (on_layer_alpha_lock_changed)
        on_layer_alpha_lock_changed(this, m_layers_container->get_child_index(target), locked);
}

void NodePanelLayer::handle_layer_blend_mode(NodeLayer* target, int mode)
{
    if (on_layer_blend_mode_changed)
        on_layer_blend_mode_changed(this, m_layers_container->get_child_index(target), mode);
}

void NodePanelLayer::handle_layer_selected(NodeLayer* target)
{
    if (m_current_layer)
        m_current_layer->m_selected = false;
    m_current_layer = target;
    m_current_layer->m_selected = true;
    if (on_layer_change)
        on_layer_change(this, -1, m_layers_container->get_child_index(m_current_layer));
    update_attributes();
}

void NodePanelLayer::clear()
{
    m_layers_container->remove_all_children();
    m_layers.clear();
    m_current_layer = nullptr;
}

void NodePanelLayer::update_attributes()
{
    auto& l = Canvas::I->m_layers[Canvas::I->m_current_layer_idx];
    m_opacity->set_value(l->m_opacity);
    m_alpha_lock->set_value(l->m_alpha_locked);
    m_blend_mode->set_index(l->m_blend_mode);
}
