#include "pch.h"
#include "legacy_canvas_draw_merge_services.h"
#include "log.h"
#include "legacy_ui_gl_dispatch.h"
#include "node_image.h"
#include "renderer_gl/opengl_capabilities.h"
#include "shader.h"
#include "app.h"

Plane NodeImage::m_plane;
Sampler NodeImage::m_sampler;
Sampler NodeImage::m_sampler_mips;

void NodeImage::static_init()
{
    m_plane.create<1>(1, 1);
    m_sampler.create();
    m_sampler_mips.create();
    m_sampler_mips.set_filter(
        pp::renderer::gl::linear_mipmap_linear_texture_filter(),
        pp::renderer::gl::linear_texture_filter());
}

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

void NodeImage::clone_copy(Node* dest) const
{
    Node::clone_copy(dest);
    NodeImage* n = static_cast<NodeImage*>(dest);
    n->m_use_atlas = m_use_atlas;
    n->m_region = m_region;
    n->m_off = m_off;
    n->m_sz = m_sz;
    n->m_path = m_path;
    n->m_tex_id = m_tex_id;
    n->m_url = m_url;
    n->m_remote_texture = m_remote_texture;
    n->m_autosize = m_autosize;
}

void NodeImage::create()
{
    Node::create();
    if (!m_path.empty() && TextureManager::load(m_path.c_str(), m_use_mipmaps))
    {
        //LOG("load image node %s", m_path.c_str());
        auto tex_sz = TextureManager::get(m_tex_id).size();
        m_off = xy(m_region) / tex_sz;
        m_sz = (zw(m_region) - xy(m_region)) / tex_sz;
        if (m_autosize)
            SetAspectRatio(tex_sz.x / tex_sz.y);
    }
}

void NodeImage::restore_context()
{
    Node::restore_context();
    create();
}

void NodeImage::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
{
    Node::parse_attributes(ka, attr);
    switch (ka)
    {
    case kAttribute::Mips:
        m_use_mipmaps = attr->BoolValue();
        break;
    case kAttribute::Path:
        m_path = attr->Value();
        m_tex_id = const_hash(attr->Value());
        break;
    case kAttribute::Url:
        m_url = attr->Value();
        break;
    case kAttribute::Region:
    {
        glm::vec4 v;
        int n = sscanf(attr->Value(), "%f %f %f %f", &v.x, &v.y, &v.z, &v.w);
        if (n == 4)
        {
            m_region = v;
            m_use_atlas = true;
        }
        break;
    }
    case kAttribute::AutoSize:
        m_autosize = attr->BoolValue();
        break;
    default:
        break;
    }
}

void NodeImage::draw()
{
    m_remote_texture ? 
        m_remote_texture->bind() : 
        TextureManager::get(m_tex_id).bind();

    auto& sampler = m_use_mipmaps ? m_sampler_mips : m_sampler;
    sampler.bind(0);

    pp::legacy::ui_gl::set_blend_enabled(true, "NodeImage");
    if (m_use_atlas)
    {
        pp::panopainter::setup_legacy_canvas_draw_merge_atlas_shader(
            {.mvp = m_mvp * glm::scale(glm::vec3(m_scale, 1.f)),
             .texture_slot = 0,
             .offset = m_off,
             .size = m_sz});
    }
    else
    {
        pp::panopainter::setup_legacy_canvas_draw_merge_texture_shader(
            {.mvp = m_mvp * glm::scale(glm::vec3(m_scale, 1.f)), .texture_slot = 0});
    }
    m_plane.draw_fill();
    sampler.unbind();
    pp::legacy::ui_gl::set_blend_enabled(false, "NodeImage");
}

bool NodeImage::set_image(const std::string& path)
{
    m_path = path;
    m_tex_id = const_hash(path.c_str());
    if (!m_path.empty() && TextureManager::load(m_path.c_str(), m_use_mipmaps))
    {
        auto tex_sz = TextureManager::get(m_tex_id).size();
        m_off = xy(m_region) / tex_sz;
        m_sz = (zw(m_region) - xy(m_region)) / tex_sz;
        if (m_autosize)
            SetAspectRatio(tex_sz.x / tex_sz.y);
        return true;
    }
    return false;
}

void NodeImage::load_url(const std::string& url)
{
    m_remote_header_decoded = false;
    m_remote_asset = std::make_shared<Asset>();
    m_remote_asset->open_url_async(url, 
        [this] (float progress) -> bool {
            int w, h, c;
            if (!m_remote_header_decoded && 
                stbi_info_from_memory(m_remote_asset->tmp_data.data(), m_remote_asset->tmp_data.size(), &w, &h, &c))
            {
                if (m_autosize)
                    SetAspectRatio((float)w / (float)h);
                m_remote_header_decoded = true;
            }
            return true;
        },
        [this] (bool success) {
            if (success)
            {
                int w, h, c;
                uint8_t* rgba = stbi_load_from_memory(m_remote_asset->m_data, m_remote_asset->m_len, &w, &h, &c, 4);
                m_remote_texture = std::make_shared<Texture2D>();
                m_remote_texture->create(
                    w,
                    h,
                    pp::renderer::gl::rgba8_internal_format(),
                    pp::renderer::gl::rgba_pixel_format(),
                    rgba);
                if (m_use_mipmaps)
                    m_remote_texture->create_mipmaps();
                delete rgba;
                if (m_autosize)
                    SetAspectRatio((float)w / (float)h);
            }
            m_remote_asset.reset();
        }
    );
}

void NodeImage::added(Node* parent)
{
    Node::added(parent);
    if (!m_url.empty() && added_to_root())
        load_url(m_url);
}
