#include "pch.h"
#include "legacy_ui_node_loader.h"

#include "legacy_ui_node_attributes.h"
#include "log.h"
#include "node.h"
#include "layout.h"
#include "node_about.h"
#include "node_border.h"
#include "node_button.h"
#include "node_button_custom.h"
#include "node_canvas.h"
#include "node_changelog.h"
#include "node_checkbox.h"
#include "node_color_quad.h"
#include "node_colorwheel.h"
#include "node_combobox.h"
#include "node_dialog_browse.h"
#include "node_dialog_cloud.h"
#include "node_dialog_picker.h"
#include "node_icon.h"
#include "node_image.h"
#include "node_image_texture.h"
#include "node_metadata.h"
#include "node_panel_animation.h"
#include "node_panel_brush.h"
#include "node_panel_color.h"
#include "node_panel_grid.h"
#include "node_panel_layer.h"
#include "node_panel_quick.h"
#include "node_panel_stroke.h"
#include "node_popup_menu.h"
#include "node_scroll.h"
#include "node_slider.h"
#include "node_stroke_preview.h"
#include "node_text.h"
#include "node_text_input.h"
#include "node_tool_bucket.h"
#include "node_usermanual.h"
#include "node_viewport.h"
#include "util.h"

namespace pp::panopainter {
namespace {

bool should_skip_child_for_os(const tinyxml2::XMLElement& x_child)
{
    if (auto os = x_child.Attribute("os"))
    {
        auto osv = split(os, ',');
        if (std::find(osv.begin(), osv.end(), PP_OS) == osv.end())
        {
            LOG("Element %s not for this os(%s), skipping", x_child.Name(), PP_OS)
            return true;
        }
    }

    return false;
}

void load_ref_child(Node& parent, const tinyxml2::XMLElement& x_child)
{
    auto ids = x_child.Attribute("id");
    auto id = const_hash(ids);
    auto& ref = (*parent.m_manager)[id]->m_children[0];
    auto n = ref->clone();
    n->m_nodeID_s = ids;
    n->m_nodeID = id;
    parent.add_child(n);
}

void load_text_child(Node& parent, const tinyxml2::XMLElement& x_child)
{
    auto n = new NodeText();
    parent.add_child(n);
    n->load_internal(&x_child, true);

    std::string text;
    auto node = x_child.FirstChild();
    while (node)
    {
        if (auto e = node->ToElement())
        {
            if (strcmp(e->Name(), "br") == 0)
                text.append("\n");
        }
        else if (auto t = node->ToText())
        {
            text.append(t->Value());
        }
        node = node->NextSibling();
    }

    if (!text.empty())
        n->set_text(text);
}

Node* instantiate_child_node(std::string_view node_name)
{
    if (node_name == "node") return new Node();
    if (node_name == "border") return new NodeBorder();
    if (node_name == "image") return new NodeImage();
    if (node_name == "image-texture") return new NodeImageTexture();
    if (node_name == "icon") return new NodeIcon();
    if (node_name == "text-input") return new NodeTextInput();
    if (node_name == "button") return new NodeButton();
    if (node_name == "button-custom") return new NodeButtonCustom();
    if (node_name == "combobox") return new NodeComboBox();
    if (node_name == "slider-h") return new NodeSliderH();
    if (node_name == "slider-v") return new NodeSliderV();
    if (node_name == "slider-hue") return new NodeSliderHue();
    if (node_name == "popup-menu") return new NodePopupMenu();
    if (node_name == "viewport") return new NodeViewport();
    if (node_name == "checkbox") return new NodeCheckBox();
    if (node_name == "layer") return new NodeLayer();
    if (node_name == "panel-layer") return new NodePanelLayer();
    if (node_name == "panel-brush") return new NodePanelBrush();
    if (node_name == "panel-color") return new NodePanelColor();
    if (node_name == "panel-stroke") return new NodePanelStroke();
    if (node_name == "panel-grid") return new NodePanelGrid();
    if (node_name == "panel-quick") return new NodePanelQuick();
    if (node_name == "dialog-browse") return new NodeDialogBrowse();
    if (node_name == "dialog-browse-item") return new NodeDialogBrowseItem();
    if (node_name == "dialog-cloud") return new NodeDialogCloud();
    if (node_name == "dialog-cloud-item") return new NodeDialogCloudItem();
    if (node_name == "color-picker") return new NodeColorPicker();
    if (node_name == "about") return new NodeAbout();
    if (node_name == "changelog") return new NodeChangelog();
    if (node_name == "usermanual") return new NodeUserManual();
    if (node_name == "tool-bucket") return new NodeToolBucket();
    if (node_name == "timeline") return new NodeAnimationTimeline();
    if (node_name == "stroke-preview") return new NodeStrokePreview();
    if (node_name == "canvas") return new NodeCanvas();
    if (node_name == "scroll") return new NodeScroll();
    if (node_name == "metadata") return new NodeMetadata();
    if (node_name == "colorwheel") return new NodeColorWheel();
    if (node_name == "color-quad") return new NodeColorQuad();
    return nullptr;
}

void load_typed_child(Node& parent, const tinyxml2::XMLElement& x_child, std::string_view node_name)
{
    if (auto* n = instantiate_child_node(node_name))
    {
        parent.add_child(n);
        n->load_internal(&x_child);
        return;
    }

    LOG("instancing UNKNOWN node: %s", x_child.Name());
    auto* n = new Node();
    parent.add_child(n);
    n->load_internal(&x_child);
}

} // namespace

void load_legacy_ui_node(Node& node, const tinyxml2::XMLElement& x_node, bool skip_children)
{
    node.m_name = x_node.Name();
    //LOG("load_internal node %s", node.m_name.c_str());

    node.init();

    auto attr = x_node.FirstAttribute();
    while (attr)
    {
        parse_legacy_ui_node_attribute(node, (kAttribute)const_hash(attr->Name()), attr);
        attr = attr->Next();
    }

    node.create();

    if (skip_children)
    {
        node.loaded();
        return;
    }

    load_legacy_ui_children(node, x_node);
    node.loaded();
}

void load_legacy_ui_children(Node& parent, const tinyxml2::XMLElement& x_node)
{
    auto x_child = x_node.FirstChildElement();
    while (x_child)
    {
        if (should_skip_child_for_os(*x_child))
        {
            x_child = x_child->NextSiblingElement();
            continue;
        }

        std::string_view node_name = x_child->Name();
        if (node_name == "ref")
            load_ref_child(parent, *x_child);
        else if (node_name == "text")
            load_text_child(parent, *x_child);
        else
            load_typed_child(parent, *x_child, node_name);

        x_child = x_child->NextSiblingElement();
    }
}

} // namespace pp::panopainter
