#include "pch.h"
#include "log.h"
#include "node_dialog_cloud.h"
#include "canvas.h"
#include "node_image_texture.h"
#include "asset.h"
#include "node_message_box.h"
#include "app.h"
#include "image.h"
#include "legacy_ui_overlay_services.h"
#include "platform_api/network_tls_policy.h"

namespace
{
bool load_cloud_thumb(CURL* curl, const std::string& name, NodeDialogCloudItem* node, std::string& response)
{
    response.clear();
    char* url_escaped = curl_easy_escape(curl, name.c_str(), (int)name.size());
    std::string url = std::string("https://panopainter.com/cloud/cloud-info.php?file=") + url_escaped;
    delete url_escaped;
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    LOG("%s", url.c_str());
    auto err = curl_easy_perform(curl);
    if (err != CURLE_OK)
        return false; // TODO: handle this error with a message or something

    auto info = split(response, ',', 3);
    int width = atoi(info[0].c_str());
    int height = atoi(info[1].c_str());
    int comp = atoi(info[2].c_str());
    assert(comp == 4);
    std::string rgb;
    rgb.resize(Base64::DecodedLength(info[3]));
    Base64::Decode(info[3], &rgb);
    Image thumb;
    thumb.create(width, height);
    thumb.copy_from((uint8_t*)rgb.data());

    auto image_tex = node->find<NodeImageTexture>("thumb-tex");
    image_tex->tex = std::make_shared<Texture2D>();
    image_tex->tex->create(thumb);

    node->app_redraw();
    return true;
}
} // namespace

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

void NodeDialogCloud::clone_finalize(Node* dest) const
{
    NodeDialogCloud* n = static_cast<NodeDialogCloud*>(dest);
    n->init_controls();
}

void NodeDialogCloud::init()
{
    init_template_file("data/dialogs/cloud-browse.xml", "dialog-cloud");
    init_controls();
}

void NodeDialogCloud::init_controls()
{
    btn_ok = find<NodeButton>("btn-ok");
    btn_cancel = find<NodeButton>("btn-cancel");
    pp::panopainter::bind_legacy_click_destroys_node(*btn_cancel, *this);
    container = find<Node>("files-list");
    load_thumbs_worker_ = std::jthread([this](std::stop_token stop) {
        load_thumbs_thread(stop);
    });
}

void NodeDialogCloud::loaded()
{
}

void NodeDialogCloud::removed(Node* parent)
{
    NodeBorder::removed(parent);
    closed = true;
    load_thumbs_worker_.request_stop();
}

NodeText* NodeDialogCloud::create_loading_status_text()
{
    auto* align = container->add_child<Node>();
    align->SetWidthP(100.f);
    align->SetHeightP(100.f);
    align->SetAlign(YGAlignCenter);
    align->SetJustify(YGJustifyCenter);
    auto* text = align->add_child<NodeText>();
    text->set_font_size(30);
    text->set_text("Connecting to the server...");
    return text;
}

bool NodeDialogCloud::load_cloud_file_list(CURL* curl, std::string& response, NodeText& status_text)
{
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_data_handler);
    curl_easy_setopt(curl, CURLOPT_URL, "https://panopainter.com/cloud/cloud-list.php");
    if (pp::platform::default_disables_network_tls_verification())
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);

    const auto err = curl_easy_perform(curl);
    if (err != CURLE_OK)
    {
        LOG("connection error: %d", err);
        status_text.set_text("Could not connect to the server");
        return false;
    }

    return true;
}

std::vector<NodeDialogCloudItem*> NodeDialogCloud::create_cloud_file_items(const std::vector<std::string>& names)
{
    std::vector<NodeDialogCloudItem*> nodes;
    nodes.reserve(names.size());

    for (const auto& name : names)
    {
        auto node = new NodeDialogCloudItem;
        node->set_manager(m_manager);
        node->init();
        node->m_text->set_text(name.c_str());
        node->m_path = App::I->work_path + "/" + name;
        node->m_file_name = name;
        container->add_child(node);
        node->on_selected = [&](NodeDialogCloudItem* target) {
            if (target == current)
                return;
            selected_path = target->m_path;
            selected_file = target->m_file_name;
            selected_name = selected_file.substr(0, selected_file.length() - strlen(".ppi"));
            if (current)
                current->m_selected = false;
            current = target;
        };
        nodes.push_back(node);
    }

    return nodes;
}

void NodeDialogCloud::load_thumbs_thread(std::stop_token stop)
{
#if WITH_CURL
    BT_SetTerminate();
    auto curl = std::unique_ptr<CURL, decltype(&curl_easy_cleanup)>(curl_easy_init(), curl_easy_cleanup);
    std::string res;
    if (curl)
    {
        if (stop.stop_requested() || closed)
            return;

        auto* text = create_loading_status_text();
        auto* align = text->m_parent;

        if (!load_cloud_file_list(curl.get(), res, *text))
        {
            return;
        }
        
        pp::panopainter::destroy_legacy_node(*align);

        if (stop.stop_requested() || closed)
            return;

        LOG("CLOUD LIST: %s", res.c_str());

        auto names = split(res, ',');
        auto nodes = create_cloud_file_items(names);

        // load the icons
        for (int i = 0; i < names.size(); i++)
        {
            const auto& n = names[i];
            auto* node = nodes[i];
            if (stop.stop_requested() || closed)
                break;

            if (!load_cloud_thumb(curl.get(), n, node, res))
                break;
        }
    }
#endif //CURL
}

//////////////////////////////////////////////////////////////////

Node* NodeDialogCloudItem::clone_instantiate() const
{
    return new NodeDialogCloudItem;
}
void NodeDialogCloudItem::clone_finalize(Node* dest) const
{
    NodeDialogCloudItem* n = static_cast<NodeDialogCloudItem*>(dest);
    n->init_controls();
}
void NodeDialogCloudItem::init()
{
    init_template_file("data/dialogs/cloud-browse.xml", "dialog-cloud-item");
    init_controls();
}
void NodeDialogCloudItem::init_controls()
{
    m_text = find<NodeText>("title");
}
void NodeDialogCloudItem::loaded()
{

}
void NodeDialogCloudItem::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();
}
kEventResult NodeDialogCloudItem::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);
        break;
    case kEventType::MouseUpL:
        break;
    default:
        return kEventResult::Available;
        break;
    }
    return kEventResult::Consumed;
}
