#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <cstdlib>
#include "effect.h"
#include "emitter.h"
#include "collection.h"
#include "particle.h"
#include "manager.h"
#include <log.h>

namespace render{
namespace fx{

emitter::emitter(effect *root):drawCount(_drawCount)
{
    _front = &_rdata[0];
    _back = &_rdata[1];
    _oldTime = 0;
    _spawnCount = 0;
    _root = root;
    _updated = 1;
    _running = 0;
}

emitter::~emitter()
{
    removeEmitterFromQueue(this);
    _locker.lock();
    _running = 0;
    _rdata[0].clear();
    _rdata[1].clear();
    _data.clear();
}

bool emitter::lock()
{
    //if(!this) return 0;
    return _locker.try_lock();
}

void emitter::getInfo(particlesUpdateInfo *out) noexcept
{
    out->drawCount = &_updatedCount;
    out->oldTime = &_oldTime;
    out->workTime = workTime;
    out->particles = &particles;
    out->emission = &emission;
    out->data = &_data;
    out->rdata = _back;
    out->locker = &_locker;
    out->texNodes = _texNodes.data();
    out->time = &time;
    out->pos = _pos+_root->pos;
    out->oldPos = &_oldPos;
    out->updated = &_updated;
    out->spawnCount = &_spawnCount;
    out->looped = _root->looped;
    out->running = _running;
}

void emitter::update() noexcept
{
    if(_locker.try_lock()) {
        if(_updated) {
            if(time >= 1)
                _running = 0;
            _pos = pos;
            _drawCount = _updatedCount;
            _updatedCount = 0;
            _updated = 0;
            std::swap(_front, _back);
            if(_front->size() > _back->size())
                _back->resize(_front->size());
            _locker.unlock();
            pushEmitterToQueue(this);
        } else {
            _locker.unlock();
        }
    }
}

void emitter::_draw() noexcept
{
    if(!_drawCount || !_atlas)
        return;
    _atlas->bind();
    render::setBlendMode(blend);
    ::render::fx::draw(_front->data(), _drawCount);
}

void emitter::start()
{
    _pos = pos;
    _oldPos = _pos+_root->pos;
    time = 0;
    _oldTime = glfwGetTime();
    _running = 1;
}

void emitter::stop()
{
    _running = 0;
}

object *emitterInfo::create(float time, effect *r)
{
    emitter *em = new emitter(r);
    emission.math(&em->emission.emission);
    direction.math(&em->emission.direction);
    lifeTime.math(&em->emission.lifeTime);

    if(spawnerInfo) {
        em->emission.spawner = spawnerInfo->create();
    } else {
        em->emission.spawner = nullptr;
    }

    em->particles.color = this->color;
    em->particles.alpha = alpha;
    em->particles.angle = angle;
    em->particles.scale = scale;
    em->particles.speed = speed;

    em->_atlas = root->getAtlas(texAtlas);
    if(!em->_atlas)
    {
        Logger::Error("Failed to find texture atlas %s\n", texAtlas.c_str());
        throw std::runtime_error("Failed to find texture atlas");
    }

    for(const auto &i:texNodes)
        em->_texNodes.push_back(em->_atlas->getTexCoord(em->_atlas->getNodeId(i)));
    for(auto &i: animation)
    {
        em->particles.texAnim.emplace_back();
        em->particles.texAnim.back().setValues(i.nodes.data(), i.nodes.size());
        em->particles.texAnim.back().time = i.time;
    }
    em->workTime = time;
    em->_updated = 1;
    return em;
}

emitterInfo::emitterInfo()
{
    spawnerInfo = 0;
}

emitterInfo::~emitterInfo()
{
    if(spawnerInfo)
        delete spawnerInfo;
}

}}
