#include "pch.h"

#include "legacy_brush_panel_services.h"

#include "app.h"
#include "asset.h"
#include "legacy_brush_ui_services.h"
#include "node_panel_brush.h"

#include <fstream>
#include <functional>
#include <memory>

void NodePanelBrush::execute_texture_list_plan(const pp::app::BrushTextureListPlan& plan)
{
    const auto status = pp::panopainter::execute_legacy_brush_texture_list_plan(*this, plan);
    if (!status.ok()) {
        LOG("Brush texture list action failed: %s", status.message);
    }
}

int NodePanelBrush::find_brush(const std::string& name) const
{
    return pp::panopainter::LegacyBrushPanelServices(const_cast<NodePanelBrush&>(*this)).find_brush(name);
}

std::string NodePanelBrush::get_texture_path(int index) const
{
    return pp::panopainter::LegacyBrushPanelServices(const_cast<NodePanelBrush&>(*this)).get_texture_path(index);
}

std::string NodePanelBrush::get_thumb_path(int index) const
{
    return pp::panopainter::LegacyBrushPanelServices(const_cast<NodePanelBrush&>(*this)).get_thumb_path(index);
}

bool NodePanelBrush::save()
{
    return pp::panopainter::LegacyBrushPanelServices(*this).save();
}

bool NodePanelBrush::restore()
{
    return pp::panopainter::LegacyBrushPanelServices(*this).restore();
}

void NodePanelBrush::clear()
{
    pp::panopainter::LegacyBrushPanelServices(*this).clear();
}

void NodePanelBrush::scan()
{
    pp::panopainter::LegacyBrushPanelServices(*this).scan();
}

void NodePanelBrush::reload()
{
    pp::panopainter::LegacyBrushPanelServices(*this).reload();
}

namespace pp::panopainter {

LegacyBrushPanelServices::LegacyBrushPanelServices(NodePanelBrush& panel) noexcept
    : panel_(panel)
{
}

int LegacyBrushPanelServices::find_brush(const std::string& name) const
{
    for (int i = 0; i < panel_.m_container->m_children.size(); i++) {
        auto* b = static_cast<NodeButtonBrush*>(panel_.m_container->m_children[i].get());
        if (b->brush_name.find(name) != std::string::npos) {
            return i;
        }
    }
    return -1;
}

std::string LegacyBrushPanelServices::get_texture_path(int index) const
{
    if (index < 0 || index >= panel_.m_container->m_children.size()) {
        return "";
    }
    return static_cast<NodeButtonBrush*>(panel_.m_container->m_children[index].get())->high_path;
}

std::string LegacyBrushPanelServices::get_thumb_path(int index) const
{
    if (index < 0 || index >= panel_.m_container->m_children.size()) {
        return "";
    }
    return static_cast<NodeButtonBrush*>(panel_.m_container->m_children[index].get())->thumb_path;
}

bool LegacyBrushPanelServices::save()
{
    std::ofstream f(App::I->data_path + "/settings/" + panel_.m_dir_name + ".bin", std::ios::binary);
    if (f.good()) {
        BinaryStreamWriter sw;
        sw.init();
        sw.wstring_raw("PPVR"); // magic code
        sw.wu16(0); // version major
        sw.wu16(1); //         minor
        sw.wu32((int)panel_.m_container->m_children.size()); // number of items
        for (const auto& child : panel_.m_container->m_children) {
            auto b = std::static_pointer_cast<NodeButtonBrush>(child);
            sw << *b;
        }
        f.write((char*)sw.m_data.data(), sw.m_data.size());
        f.close();
        App::I->flush_platform_storage();
        return true;
    }
    return false;
}

bool LegacyBrushPanelServices::restore()
{
    Asset f;
    auto path = App::I->data_path + "/settings/" + panel_.m_dir_name + ".bin";
    if (f.open(path.c_str())) {
        f.read_all();

        if (f.m_len == 0) {
            return false;
        }

        BinaryStreamReader sr;
        sr.init(f.m_data, f.m_len);

        if (sr.rstring(4) != "PPVR") {
            LOG("PPVR tag not found")
                return false;
        }
        auto vmaj = sr.ru16();
        auto vmin = sr.ru16();
        if (vmaj != 0 && vmin != 1) {
            LOG("unrecognised version %d.%d", vmaj, vmin);
            return false;
        }

        auto count = sr.ru32();

        for (int k = 0; k < count; k++) {
            auto b = std::make_shared<NodeButtonBrush>();
            if (!b->read(sr)) {
                LOG("error deserializing the button brush");
                return false;
            }

            if (Asset::exist(b->high_path)) {
                panel_.m_container->add_child(b);
                b->init();
                b->create();
                b->loaded();
                b->set_icon(b->thumb_path.c_str());
                b->on_click = std::bind(&NodePanelBrush::handle_click, &panel_, std::placeholders::_1);
            }
        }
        return true;
    }
    return false;
}

void LegacyBrushPanelServices::clear()
{
    panel_.m_container->remove_all_children();
}

void LegacyBrushPanelServices::scan()
{
    auto icons = Asset::list_files("data/" + panel_.m_dir_name, ".*\\.png$");
    for (auto& i : icons) {
        std::string path = "data/" + panel_.m_dir_name + "/thumbs/" + i;
        std::string path_hi = "data/" + panel_.m_dir_name + "/" + i;
        NodeButtonBrush* brush = new NodeButtonBrush;
        panel_.m_container->add_child(brush);
        brush->init();
        brush->create();
        brush->loaded();
        brush->set_icon(path.c_str());
        brush->thumb_path = path;
        brush->high_path = path_hi;
        brush->brush_name = i;
        brush->m_user_brush = false; // system brush, cannot be deleted from file
        brush->on_click = std::bind(&NodePanelBrush::handle_click, &panel_, std::placeholders::_1);
    }

    auto custom_icons = Asset::list_files(App::I->data_path + "/" + panel_.m_dir_name, ".*\\.png$");
    for (auto& i : custom_icons) {
        std::string path_thumb = App::I->data_path + "/" + panel_.m_dir_name + "/thumbs/" + i;
        std::string path_high = App::I->data_path + "/" + panel_.m_dir_name + "/" + i;
        NodeButtonBrush* brush = new NodeButtonBrush;
        panel_.m_container->add_child(brush);
        brush->init();
        brush->create();
        brush->loaded();
        brush->set_icon(path_thumb.c_str());
        brush->thumb_path = path_thumb;
        brush->high_path = path_high;
        brush->brush_name = i;
        brush->m_user_brush = true;
        brush->on_click = std::bind(&NodePanelBrush::handle_click, &panel_, std::placeholders::_1);
    }
}

void LegacyBrushPanelServices::reload()
{
    clear();
    scan();
    save();
}

} // namespace pp::panopainter
