#include "atlas.h"
#include <GL/glew.h>
#include <tinyxml2.h>
#include <list>
#include <log.h>
#include <cmath>

using namespace tinyxml2;

namespace render::sprite {

textureAtlas::textureAtlas(const std::string &path, bool smooth)
{
    load(path, smooth);
}

textureAtlas::~textureAtlas()
{
    free();
}

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);
}

#define CheckAttribute(x, y, z) \
        if(z->QueryAttribute(x, y))\
            Logger::Warning("Can't find attribute %s\n", x);


bool textureAtlas::load(const std::string &path, bool smooth, bool flip)
{
    Logger::Debug("Loading texture atlas : %s\n", path.c_str());
    tinyxml2::XMLDocument document;

    if(document.LoadFile(path.c_str())) {
        return false;
    }

    free();

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

    if(!texture::load(path.substr(0,path.rfind('/')+1)+element->Attribute("imagePath"), smooth, flip)) {
        return false;
    }

    parameteri(GL_TEXTURE_WRAP_S, GL_CLAMP);
    parameteri(GL_TEXTURE_WRAP_T, GL_CLAMP);
    element = element->FirstChildElement("sprite");

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

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

        const char *name = element->Attribute("n");
        if(name) {
            temp.name = name;
        } else {
            Logger::Warning("Can't find attribute \"n\"\n");
        }

        mathRotate(temp);
        mathUV(temp);

        elements.push_back(temp);
    }

    return true;
}

void textureAtlas::mathRotate(textureAtlasNode &p) noexcept
{
    vec2f offset[6] = {
        vec2f{0, 1}, vec2f{1, 1}, vec2f{1, 0},
        vec2f{1, 0}, vec2f{0, 0}, vec2f{0, 1}
    };

    float msin, mcos;
    sincosf(p.angle, &msin, &mcos);

    for (int i = 0; i < 6; 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[6] = {
        vec2f{0, 1}, vec2f{1, 1}, vec2f{1, 0},
        vec2f{1, 0}, vec2f{0, 0}, vec2f{0, 1}
    };

    float msin, mcos;
    sincosf(p.angle, &msin, &mcos);

    for (int i = 0; i < 6; 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;
}

void textureAtlas::mathUV(textureAtlasNode &p) noexcept
{
    static constexpr vec2f offset[6] = {
        vec2f{0, 1}, vec2f{1, 1}, vec2f{1, 0},
        vec2f{1, 0}, vec2f{0, 0}, vec2f{0, 1}
    };

    for(int i = 0; i < 6; i++) {
        p.uv[i] = (p.pos + offset[i] * p.size) / _size;
    }
}

void textureAtlas::mathUV(icon &p) const noexcept
{
    static constexpr vec2f offset[6] = {
        vec2f{0, 1}, vec2f{1, 1}, vec2f{1, 0},
        vec2f{1, 0}, vec2f{0, 0}, vec2f{0, 1}
    };

    for(int i = 0; i < 6; i++) {
        p._uv[i] = (p._pos + offset[i] * p._isize) / _size;
    }
}

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();
}

icon textureAtlas::getIcon(uint32_t c) const noexcept
{
    icon temp;
    if(c >= elements.size()) {
        return temp;
    }

    temp = elements[c];
    temp._parent = this;
    return temp;
}
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);
    memcpy(_uv, node.uv, sizeof(vec2f)*6);
	return *this;
}

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

}
