/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */


#ifndef ECXX_CTL_GFX_COLOR_ANIMATION_1D_PROC_RANDOM_STARLIGHT_H
#define ECXX_CTL_GFX_COLOR_ANIMATION_1D_PROC_RANDOM_STARLIGHT_H


#include <eCxx/CTL/GFX/ColorGenerator1D_OffOn.h>


namespace eCxx {


//
// A simple 1-dimensional animation processor class - random starlight using ColorGenerator1D<T>
//
template <
    typename ColorAnimator1D_T,
    typename ColorGeneratorImplementationT,
    uint8_t  StarPointCountN,
    typename AnimProcColorT = typename ColorAnimator1D_T::ColorFX1DType::BufferColorType
>
class Animation1DProc_RandomStarlight : public ColorAnimator1D_T::Animation1DProcType {
    static_assert(
        (StarPointCountN >= 1) && (StarPointCountN <= 250),
        "template argument substituting StarPointCountN is not between 1 and 250"
    );

    static_assert(
        StarPointCountN <= ColorAnimator1D_T::ColorFX1DType::BufferElementCount,
        "template argument substituting StarPointCountN is larger than the number of elements contained by the type that substitutes the template argument ColorAnimator1D_T::ColorFX1DType"
    );

    public:
        using AnimProcColorType    = AnimProcColorT;
        using ColorAnimator1DType  = ColorAnimator1D_T;
        using ColorFX1DType        = typename ColorAnimator1D_T::Animation1DProcType::ColorFX1DType;
        using BufferColorType      = typename ColorAnimator1D_T::Animation1DProcType::BufferColorType;
        using BufferSizeType       = typename ColorAnimator1D_T::Animation1DProcType::BufferSizeType;

        using ColorGenerator1DType = ColorGenerator1D_CRTPBase<ColorGeneratorImplementationT, AnimProcColorType, BufferSizeType>;

        static constexpr uint8_t StarPointCount = StarPointCountN;

    public:
        inline Animation1DProc_RandomStarlight(ColorGenerator1DType& colorGen);
        inline virtual ~Animation1DProc_RandomStarlight();

        virtual void reset(ColorFX1DType& colorFX1D, BufferSizeType elemBeginIdx, BufferSizeType elemEndIdx);
        virtual void update(ColorFX1DType& colorFX1D, BufferSizeType elemBeginIdx, BufferSizeType elemEndIdx);

#if defined(__x86_64__) || defined(__i386__)
        // NOTE : Some color generator implementations which are used by this animation
        //        processor implementation may require reset() to be called first for this
        //        function to work properly
        virtual CBP::QUInt estimatedTotalAnimationFrameCount() const;
#endif

    private:
        struct StarPoint {
            BufferSizeType p;
            uint8_t        state;
            int8_t         speed;
        };

        static constexpr int8_t StarSpeed_L =  5; // The speeds must be selected in a way
        static constexpr int8_t StarSpeed_M = 10; // so that 100 is divisible by them
        static constexpr int8_t StarSpeed_H = 20; //

    private:
        ColorGenerator1DType* const _colorGen;

        StarPoint                   _starPoint[StarPointCount];
};


template <typename ColorAnimator1D_T, typename ColorGeneratorImplementationT, uint8_t StarPointCountN, typename AnimProcColorT>
inline Animation1DProc_RandomStarlight<ColorAnimator1D_T, ColorGeneratorImplementationT, StarPointCountN, AnimProcColorT>
::Animation1DProc_RandomStarlight(ColorGenerator1DType& colorGen)
: _colorGen(&colorGen)
{}


template <typename ColorAnimator1D_T, typename ColorGeneratorImplementationT, uint8_t StarPointCountN, typename AnimProcColorT>
inline Animation1DProc_RandomStarlight<ColorAnimator1D_T, ColorGeneratorImplementationT, StarPointCountN, AnimProcColorT>
::~Animation1DProc_RandomStarlight()
{}


template <typename ColorAnimator1D_T, typename ColorGeneratorImplementationT, uint8_t StarPointCountN, typename AnimProcColorT>
void Animation1DProc_RandomStarlight<ColorAnimator1D_T, ColorGeneratorImplementationT, StarPointCountN, AnimProcColorT>
::reset(ColorFX1DType&, BufferSizeType, BufferSizeType)
{
    _colorGen->reset();
    memset( _starPoint, 0, sizeof(_starPoint) );
}


template <typename ColorAnimator1D_T, typename ColorGeneratorImplementationT, uint8_t StarPointCountN, typename AnimProcColorT>
void Animation1DProc_RandomStarlight<ColorAnimator1D_T, ColorGeneratorImplementationT, StarPointCountN, AnimProcColorT>
::update(ColorFX1DType& colorFX1D, BufferSizeType elemBeginIdx, BufferSizeType elemEndIdx)
{
    for(uint8_t i = 0; i < StarPointCount; ++i) {
        // Get the current star point
        StarPoint& sp = _starPoint[i];
        // Create a new star point as needed
        if(!sp.speed) {
            sp.p     = Application::random16(0, ColorFX1DType::BufferElementCount - 1);
            sp.state = 0;
            switch( Application::random8(0, 2) ) {
                case 0 : sp.speed = StarSpeed_L; break;
                case 1 : sp.speed = StarSpeed_M; break;
                case 2 : sp.speed = StarSpeed_H; break;
            }
        }
        // Draw the star point
        colorFX1D.set(
            sp.p,
            _colorGen->generate(
                elemBeginIdx, elemEndIdx, sp.p, sp.state
            )
        );
        // Update the star point as needed
        if(sp.speed > 0) {
            if(sp.state < 100) {
                const int16_t newState = static_cast<int16_t>(sp.state) + sp.speed;
                sp.state = (newState <= 100) ? newState : 100;
            }
            else {
                sp.speed = -sp.speed;
            }
        }
        else if(sp.speed < 0) {
            if(sp.state > 0) {
                const int16_t newState = static_cast<int16_t>(sp.state) + sp.speed;
                sp.state = (newState >= 0) ? newState : 0;
            }
            else {
                sp.speed = 0;
            }
        }
    }
}


#if defined(__x86_64__) || defined(__i386__)

template <typename ColorAnimator1D_T, typename ColorGeneratorImplementationT, uint8_t StarPointCountN, typename AnimProcColorT>
CBP::QUInt Animation1DProc_RandomStarlight<ColorAnimator1D_T, ColorGeneratorImplementationT, StarPointCountN, AnimProcColorT>
::estimatedTotalAnimationFrameCount() const
{ return (100 / StarSpeed_L * 2 + 2) * 5; }

#endif


//
// A simple 1-dimensional animation processor class - random starlight between off and on colors
//
template <
    typename ColorAnimator1D_T,
    uint8_t  StarPointCountN,
    typename AnimProcColorT     = typename ColorAnimator1D_T::ColorFX1DType::BufferColorType,
    uint8_t  AnimChannelIndexes = AnimProcColorT::A
>
class Animation1DProc_RandomStarlightTwoColors : public Animation1DProc_RandomStarlight<
    ColorAnimator1D_T,
    ColorGenerator1D_OffOn<AnimProcColorT, typename ColorAnimator1D_T::Animation1DProcType::BufferSizeType>,
    StarPointCountN,
    AnimProcColorT
> {
    public:
        using Animation1DProcType      = Animation1DProc_RandomStarlight<
                                             ColorAnimator1D_T,
                                             ColorGenerator1D_OffOn<
                                                 AnimProcColorT,
                                                 typename ColorAnimator1D_T::Animation1DProcType::BufferSizeType
                                             >,
                                             StarPointCountN,
                                             AnimProcColorT
                                         >;

        using ColorGenerator1DImplType = ColorGenerator1D_OffOn<
                                             AnimProcColorT,
                                             typename ColorAnimator1D_T::Animation1DProcType::BufferSizeType,
                                             AnimChannelIndexes
                                         >;

        using AnimProcColorType    = typename Animation1DProcType::AnimProcColorType;
        using ColorAnimator1DType  = typename Animation1DProcType::ColorAnimator1DType;
        using ColorFX1DType        = typename Animation1DProcType::ColorFX1DType;
        using BufferColorType      = typename Animation1DProcType::BufferColorType;
        using BufferSizeType       = typename Animation1DProcType::BufferSizeType;

        using ColorGenerator1DType = typename Animation1DProcType::ColorGenerator1DType;

    public:
        inline Animation1DProc_RandomStarlightTwoColors(const AnimProcColorType& colorOff, const AnimProcColorType& colorOn);
        inline virtual ~Animation1DProc_RandomStarlightTwoColors();

    private:
        ColorGenerator1DImplType _colorGenInst;
};


template <typename ColorAnimator1D_T, uint8_t StarPointCountN, typename AnimProcColorT, uint8_t AnimChannelIndexes>
inline Animation1DProc_RandomStarlightTwoColors<ColorAnimator1D_T, StarPointCountN, AnimProcColorT, AnimChannelIndexes>
::Animation1DProc_RandomStarlightTwoColors(const AnimProcColorType& colorOff, const AnimProcColorType& colorOn)
: Animation1DProcType(_colorGenInst    )
, _colorGenInst      (colorOff, colorOn)
{}


template <typename ColorAnimator1D_T, uint8_t StarPointCountN, typename AnimProcColorT, uint8_t AnimChannelIndexes>
inline Animation1DProc_RandomStarlightTwoColors<ColorAnimator1D_T, StarPointCountN, AnimProcColorT, AnimChannelIndexes>
::~Animation1DProc_RandomStarlightTwoColors()
{}


} // namespace eCxx


#endif
