#include "pch.h"
#include "log.h"
#include "util.h"
#include "asset.h"
#include "node.h"
#include "node_border.h"
#include "legacy_ui_overlay_services.h"
#include "layout.h"
#include "platform_api/asset_file_load_policy.h"

void LayoutManager::unload()
{
    for (auto& l : m_layouts)
        pp::panopainter::destroy_legacy_node(*l.second);
    m_layouts.clear();
}

void LayoutManager::create()
{
    m_layouts[const_hash("main")] = std::make_unique<Node>();
    m_layouts[const_hash("main")]->m_manager = this;
}

bool LayoutManager::load(const char* path)
{
    auto abs_path = Asset::absolute(path);
    const auto platform_family = pp::platform::current_platform_family();
    const auto file_probe = pp::platform::platform_uses_asset_file_mtime_reload(platform_family)
        ? pp::platform::probe_asset_file_timestamp(abs_path)
        : pp::platform::AssetFileTimestampProbe {};
    const auto file_load = pp::platform::plan_asset_file_load_with_probe(
        platform_family,
        m_loaded,
        m_file_last_write_time,
        file_probe);
    if (!file_load.should_read_file)
        return file_load.skipped_load_result;
    m_file_last_write_time = file_load.last_write_time;

    if (!m_layouts.empty() && on_reloading)
        on_reloading();
    
    m_path = path;

    Asset file;
    if (!(file.open(path) && file.read_all()))
        return false;
    auto xml_string = std::string((char*)file.m_data, (size_t)file.m_len);
    file.close();

    if (!parse(xml_string))
        return false;
    
    if (on_loaded)
        on_loaded(m_loaded);
    m_loaded = true;

    return true;
}

bool LayoutManager::parse(const std::string& xml_string) noexcept
{
    auto old = std::move(m_layouts);

    tinyxml2::XMLDocument xml(true, tinyxml2::COLLAPSE_WHITESPACE);
    auto ret = xml.Parse(xml_string.c_str(), xml_string.size());
    if (ret != tinyxml2::XMLError::XML_SUCCESS)
    {
        return false;
        LOG("parsing xml failed");
    }

    tinyxml2::XMLElement* current = xml.RootElement()->FirstChildElement();
    while (current)
    {
        auto id_str = current->Attribute("id");
        if (!id_str)
        {
            LOG("Layout node without id");
            return false;
        }
        if (auto os = current->Attribute("os"))
        {
            auto osv = split(os, ',');
            if (std::find(osv.begin(), osv.end(), PP_OS) == osv.end())
            {
                LOG("Layout %s not for this os(%s), skipping", id_str, PP_OS)
                    current = current->NextSiblingElement("layout");
                continue;
            }
        }
        //LOG("Parsing layout: %s", id_str);
        uint16_t id = const_hash(id_str);
        auto p = m_layouts.find(id);
        if (p == m_layouts.end())
        {
            auto& node = m_layouts[id];
            std::string node_name = current->Name();
            node.reset(node_name == "border" ? new NodeBorder : new Node);
            node->set_manager(this);
            // try to copy the old size values
            if (old.count(id))
            {
                const auto& old_node = *old[id];
                YGNodeCopyStyle(node->y_node, old_node.y_node);
            }
            node->load_internal(current);
        }
        else
        {
            LOG("Layout id \"%s\" duplicated", id_str);
        }
        current = current->NextSiblingElement("layout");
    }
    return true;
}

bool LayoutManager::reload()
{
     // avoid conflict when assigning the same string from c_str
     std::string path_copy = m_path;
     return load(path_copy.c_str());
}

void LayoutManager::restore_context()
{
    for (auto& node : m_layouts)
        node.second->restore_context();
}

void LayoutManager::clear_context()
{
    for (auto& node : m_layouts)
        node.second->clear_context();
}
