#include "atlas.hpp"
#include <cmath>
#include <list>
#include <tinyxml2.h>

using namespace tinyxml2;

namespace render::sprite
{

TextureAtlas::TextureAtlas(const std::string &path)
{
    tinyxml2::XMLDocument document;

    document.LoadFile(path.c_str());

    tinyxml2::XMLElement *element = document.RootElement();

    element->QueryIntAttribute("width", &_size.x);
    element->QueryIntAttribute("height", &_size.y);

    std::string texturePath = path.substr(0, path.rfind('/') + 1) + element->Attribute("imagePath");

    this->texture = new kernel::Texture(texturePath.c_str());

    element = element->FirstChildElement("sprite");

    for (int i = 0; element; i++, element = element->NextSiblingElement()) {
        TextureAtlasNode temp;
        temp.angle = 0;
        temp.scale = vec2f(1);

        element->QueryAttribute("x", &temp.pos.x);
        element->QueryAttribute("y", &temp.pos.y);
        element->QueryAttribute("w", &temp.size.x);
        element->QueryAttribute("h", &temp.size.y);
        element->QueryAttribute("pX", &temp.center.x);
        element->QueryAttribute("pY", &temp.center.y);

        const char *name = element->Attribute("n");
        temp.name = name;
        temp.size += vec2f(1);

        mathRotate(temp);

        elements.push_back(temp);
    }
}

TextureAtlas::~TextureAtlas()
{
    delete this->texture;
}

vec4f TextureAtlas::getTexCoord(uint32_t c) const
{
    if (c >= elements.size()) {
        return vec4f(0, 0, 1, 1);
    }

    return vec4f(elements[c].pos / _size, elements[c].size / _size);
}

void TextureAtlas::mathRotate(TextureAtlasNode &p) noexcept
{
    vec2f offset[4] = {
        vec2f{0, 0}, vec2f{1, 0},
        vec2f{0, 1}, vec2f{1, 1}};
    float msin, mcos;
    sincosf(p.angle, &msin, &mcos);

    for (int i = 0; i < 4; i++) {
        offset[i] -= p.center;
        offset[i] *= p.size;

        p.rotMatrix[i].x = offset[i].x * mcos - offset[i].y * msin;
        p.rotMatrix[i].y = offset[i].x * msin + offset[i].y * mcos;
    }
}

void TextureAtlas::mathRotate(Icon &p) const noexcept
{
    vec2f offset[4] = {
        vec2f{0, 0}, vec2f{1, 0},
        vec2f{0, 1}, vec2f{1, 1}};
    float msin, mcos;
    sincosf(p.angle, &msin, &mcos);

    for (int i = 0; i < 4; i++) {
        offset[i] -= p.center;
        offset[i] *= p.size;

        p._rotMatrix[i].x = offset[i].x * mcos - offset[i].y * msin;
        p._rotMatrix[i].y = offset[i].x * msin + offset[i].y * mcos;
    }

    p._angle = p.angle;
    p._size = p.size;
    p._center = p.center;
}

int TextureAtlas::getNodeId(const std::string &name) const noexcept
{
    for (uint32_t i = 0; i < elements.size(); i++)
        if (elements[i].name == name)
            return i;
    return -1;
}

uint32_t TextureAtlas::getNodesCount() const noexcept
{
    return elements.size();
}

void TextureAtlas::getIcon(const std::string &name, Icon &ic) const noexcept
{
    for (auto &i : elements)
        if (i.name == name) {
            ic = i;
            ic._parent = this;
            return;
        }
}

Icon::Icon()
{
    _parent = 0;
    color = color::white;
    scale = vec2f(1);
}

void Icon::draw(const vec2f &p) noexcept
{
    if (!_parent)
        return;
    _parent->draw(p, *this);
}

void Icon::draw(const vec2f &p) const noexcept
{
    if (!_parent)
        return;
    _parent->draw(p, *this);
}

Icon &Icon::operator=(const TextureAtlasNode &node)
{
    size = _size = _isize = node.size;
    angle = _angle = node.angle;
    center = _center = node.center;
    _pos = node.pos;
    memcpy(_rotMatrix, node.rotMatrix, sizeof(vec2f) * 6);
    return *this;
}

const vec2f *Icon::getVertices() const noexcept
{
    return _rotMatrix;
}

} // namespace render::sprite
