#pragma once
#include "../../common.h"
#include <vector>
#include <memory.h>

namespace render{
namespace fx{

    template<typename T>
    class _baseParam
    {
    public:
        T linear() const noexcept;
        T quadratic() const noexcept;

        struct node
        {
            float time;
            T value;
        };
        uint32_t currentNode;
        size_t size = 0;
        float ctimer;
        node *values = nullptr;
        decltype(&_baseParam<T>::linear) interpolation;
        void resize(size_t) noexcept;
        void clear() noexcept;
        _baseParam() noexcept;
        _baseParam(const _baseParam&) noexcept;
        ~_baseParam() noexcept;
    };

    ///========================================
    template<typename T>
    inline void _baseParam<T>::resize(size_t _size) noexcept
    {
        currentNode = 0;
        if(_size == size) return;
        if(size && values)
            delete [] values;
        size = _size;
        if(!size)
            values = nullptr;
        else
            values = new node[size];
    }

    template<typename T>
    inline void _baseParam<T>::clear() noexcept
    {
        if(!size) return;
        delete [] values;
        values = nullptr;
        size = 0;
    }

    template<typename T>
    _baseParam<T>::_baseParam() noexcept
    {
		currentNode = 0;
		ctimer = 0;
        size = 0;
        interpolation = &_baseParam::linear;
    }

    template<typename T>
    _baseParam<T>::_baseParam(const _baseParam<T> &r) noexcept
    {
        interpolation = r.interpolation;
        size = r.size;
        if(!size)
            return;
        currentNode = r.currentNode;
        ctimer = r.ctimer;
        values = new node[size];
        memcpy(values, r.values, size*sizeof(node));
    }

    template<typename T>
    _baseParam<T>::~_baseParam() noexcept
    {
        if(size)
            delete [] values;
    }

    template<typename T>
    T _baseParam<T>::linear() const noexcept
    {
        return values[currentNode+1].value*ctimer+values[currentNode].value*(1.0f-ctimer);
    }

    template<typename T>
    T _baseParam<T>::quadratic() const noexcept
    {
        float t = ctimer*ctimer;
        return values[currentNode+1].value*t+values[currentNode].value*(1.0f-t);
    }

    template<typename T>
    class _listParam: public _baseParam<T>
    {
    public:
        using _baseParam<T>::ctimer;
        using _baseParam<T>::values;
        using _baseParam<T>::currentNode;
        using _baseParam<T>::size;
        using _baseParam<T>::interpolation;
        inline T math(float a) noexcept
        {
            if(!size) return 0;
            if(currentNode == size
            || size == 1)
                return values[currentNode].value;

            if(values[currentNode].time > a)
                return values[currentNode].value;

            if(values[currentNode+1].time < a)
                if(++currentNode == size-1)
                    return values[currentNode].value;

            if(interpolation == nullptr)
                return values[currentNode].value;
            _baseParam<T>::ctimer = (a-values[currentNode].time)/(values[currentNode+1].time-values[currentNode].time);
            return (this->*interpolation)();
        }
    };

    template<typename T>
    class _loopedParam: public _baseParam<T>
    {
    public:
        using _baseParam<T>::ctimer;
        using _baseParam<T>::values;
        using _baseParam<T>::currentNode;
        using _baseParam<T>::size;
        using _baseParam<T>::interpolation;
        inline T math(float a) noexcept
        {
            if(!size) return 0;
            if(size == 1)
                return values[0].value;

            currentNode = 0;

            for(size_t i = 0; i < size; i++)
            {
                if(values[i].time < a)
                    currentNode++;
                else
                    break;
            }

            if(currentNode == size-1)
                return values[currentNode].value;

            if(&interpolation == nullptr)
                return values[currentNode].value;
            ctimer = (a-values[currentNode].time)/(values[currentNode+1].time-values[currentNode].time);
            return (this->*interpolation)();
        }
    };

    class _textureAnimation
    {
    public:
        struct node
        {
            float time;
            vec4f value;
        };
        vec2u32 nodes;
        size_t size = 0;
        float ctimer;
        node *values = nullptr;

        inline void resize(size_t _size) noexcept
        {
            if(_size == size) return;
            if(size && values)
                delete [] values;
            size = _size;
            if(!size)
                values = 0;
            else
                values = new node[size];
        }

        inline void clear() noexcept
        {
            if(!size) return;
            delete [] values;
            size = 0;
        }

        _textureAnimation() noexcept
        {
			ctimer = 0;
            size = 0;
        }

        _textureAnimation(const _textureAnimation &r)
        {
            size = r.size;
            if(!size)
                return;
			ctimer = r.ctimer;
            values = new node[r.size];
            memcpy(values, r.values, size*sizeof(node));
        }

        ~_textureAnimation()
        {
            if(size)
                delete [] values;
            size = 0;
        }

        inline void math(float a) noexcept
        {
            if(!size)
                return;
            uint32_t cnode = 0;
            for(uint32_t i = 0; i < size; i++)
                if(values[i].time > a)
                {
                    if(i)
                        cnode = i-1;
                    break;
                }
                else if(i+1 == size) //последняя итерация
                    cnode = size-1;
            if(cnode == size-1)
            {
                nodes = vec2u32(cnode);
                ctimer = 0;
                return;
            }
            ctimer = (a-values[cnode].time)/(values[cnode+1].time-values[cnode].time);
            nodes = vec2u32(cnode, cnode+1);
        }
    };

    class textureIndexParam
    {
    public:
        textureIndexParam()noexcept:_size(0){};
        textureIndexParam(const textureIndexParam&);
        ~textureIndexParam() noexcept;

        void free() noexcept;
        void setValues(uint32_t *values, size_t size) noexcept;
        //const vec4f& getValue(size_t pos) const noexcept;
        uint32_t getValue() const noexcept;
        float time;
    private:
        uint32_t *_values;
        size_t _size;
    };

    template<typename T>
    class _emitterParam
    {
        struct node
        {
            float time;
            T value, spread;
            node(float t, T v, T s = 0):time(t),value(v),spread(s){};
        };
    public:
        std::vector<node> values;

        _emitterParam() = default;
        _emitterParam(const _emitterParam &a)
        {
            values = a.values;
        }

        void math(_listParam<T> *out, T(*randFunc)()) const noexcept
        {
            const node *data = values.data();
            out->resize(values.size());
            for(uint32_t i = 0; i < values.size(); i++)
            {
                out->values[i].value = data[i].value+data[i].spread*randFunc();
                out->values[i].time = data[i].time;
            }
        }

        void math(_listParam<T> *out, const T &randVal) const noexcept
        {
            const node *data = values.data();
            out->resize(values.size());
            for(uint32_t i = 0; i < values.size(); i++)
            {
                out->values[i].value = data[i].value+data[i].spread*randVal;
                out->values[i].time = data[i].time;
            }
        }

        void math(_loopedParam<T> *out) const noexcept
        {
            const node *data = values.data();
            out->resize(values.size());
            for(uint32_t i = 0; i < values.size(); i++)
            {
                out->values[i].value = data[i].value;
                out->values[i].time = data[i].time;
            }
        }
    };

}}
