/*
 * 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_H
#define ECXX_CTL_GFX_COLOR_ANIMATION_1D_PROC_RANDOM_H


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


namespace eCxx {


//
// A simple 1-dimensional animation processor class - random color(s) using ColorGenerator1D<T>
//
// NOTE : If SkipDrawingClearPixel is set to true, the corresponding ColorFX1D<T> instance
//        will need to be cleared manually, either by calling its clear() method or filling
//        its buffer via another methods.
//
template <
    typename ColorAnimator1D_T,
    typename ColorGeneratorImplementationT,
    bool     SkipDrawingClearPixel = false,
    typename AnimProcColorT        = typename ColorAnimator1D_T::ColorFX1DType::BufferColorType
>
class Animation1DProc_Random : public ColorAnimator1D_T::Animation1DProcType {
    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>;

    public:
        inline Animation1DProc_Random(ColorGenerator1DType& colorGen, uint8_t density);
        inline virtual ~Animation1DProc_Random();

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

        // NOTE : Can be called manually anytime to change the density
        inline void set(uint8_t density);

#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:
        ColorGenerator1DType* const _colorGen;
        uint8_t                     _density;
};


template <typename ColorAnimator1D_T, typename ColorGeneratorImplementationT, bool SkipDrawingClearPixel, typename AnimProcColorT>
inline Animation1DProc_Random<ColorAnimator1D_T, ColorGeneratorImplementationT, SkipDrawingClearPixel, AnimProcColorT>
::Animation1DProc_Random(ColorGenerator1DType& colorGen, uint8_t density)
: _colorGen(&colorGen)
, _density ( density )
{}


template <typename ColorAnimator1D_T, typename ColorGeneratorImplementationT, bool SkipDrawingClearPixel, typename AnimProcColorT>
inline Animation1DProc_Random<ColorAnimator1D_T, ColorGeneratorImplementationT, SkipDrawingClearPixel, AnimProcColorT>
::~Animation1DProc_Random()
{}


template <typename ColorAnimator1D_T, typename ColorGeneratorImplementationT, bool SkipDrawingClearPixel, typename AnimProcColorT>
void Animation1DProc_Random<ColorAnimator1D_T, ColorGeneratorImplementationT, SkipDrawingClearPixel, AnimProcColorT>
::reset(ColorFX1DType&, BufferSizeType, BufferSizeType)
{ _colorGen->reset(); }


template <typename ColorAnimator1D_T, typename ColorGeneratorImplementationT, bool SkipDrawingClearPixel, typename AnimProcColorT>
void Animation1DProc_Random<ColorAnimator1D_T, ColorGeneratorImplementationT, SkipDrawingClearPixel, AnimProcColorT>
::update(ColorFX1DType& colorFX1D, BufferSizeType elemBeginIdx, BufferSizeType elemEndIdx)
{
    // Set the color(s)
    for(BufferSizeType i = elemBeginIdx; i <= elemEndIdx; ++i) {
        // If configured, skip drawing clear pixel
        __if_constexpr(SkipDrawingClearPixel) {
            if(Application::random8() <= _density) {
                colorFX1D.set(
                    i,
                    _colorGen->generate(elemBeginIdx, elemEndIdx, i, true)
                );
            }
        }
        else {
            colorFX1D.set(
                i,
                _colorGen->generate( elemBeginIdx, elemEndIdx, i, Application::random8() <= _density )
            );
        }
    }
}


template <typename ColorAnimator1D_T, typename ColorGeneratorImplementationT, bool SkipDrawingClearPixel, typename AnimProcColorT>
void Animation1DProc_Random<ColorAnimator1D_T, ColorGeneratorImplementationT, SkipDrawingClearPixel, AnimProcColorT>
::set(uint8_t density)
{ _density = density; }


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

template <typename ColorAnimator1D_T, typename ColorGeneratorImplementationT, bool SkipDrawingClearPixel, typename AnimProcColorT>
CBP::QUInt Animation1DProc_Random<ColorAnimator1D_T, ColorGeneratorImplementationT, SkipDrawingClearPixel, AnimProcColorT>
::estimatedTotalAnimationFrameCount() const
{ return _colorGen->estimatedTotalAnimationFrameCount() * 50; }

#endif


//
// A simple 1-dimensional animation processor class - random between off and on colors
//
// NOTE : If SkipDrawingClearPixel is set to true, the corresponding ColorFX1D<T> instance
//        will need to be cleared manually, either by calling its clear() method or filling
//        its buffer via another methods.
//
template <
    typename ColorAnimator1D_T,
    bool     SkipDrawingClearPixel = false,
    typename AnimProcColorT        = typename ColorAnimator1D_T::ColorFX1DType::BufferColorType,
    uint8_t  AnimChannelIndexes    = AnimProcColorT::A
>
class Animation1DProc_RandomTwoColors : public Animation1DProc_Random<
    ColorAnimator1D_T,
    ColorGenerator1D_OffOn<AnimProcColorT, typename ColorAnimator1D_T::Animation1DProcType::BufferSizeType>,
    SkipDrawingClearPixel,
    AnimProcColorT
> {
    public:
        using Animation1DProcType      = Animation1DProc_Random<
                                             ColorAnimator1D_T,
                                             ColorGenerator1D_OffOn<
                                                 AnimProcColorT,
                                                 typename ColorAnimator1D_T::Animation1DProcType::BufferSizeType
                                             >,
                                             SkipDrawingClearPixel,
                                             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_RandomTwoColors(const AnimProcColorType& colorOff, const AnimProcColorType& colorOn, uint8_t density);
        inline virtual ~Animation1DProc_RandomTwoColors();

    private:
        ColorGenerator1DImplType _colorGenInst;
};


template <typename ColorAnimator1D_T, bool SkipDrawingClearPixel, typename AnimProcColorT, uint8_t AnimChannelIndexes>
inline Animation1DProc_RandomTwoColors<ColorAnimator1D_T, SkipDrawingClearPixel, AnimProcColorT, AnimChannelIndexes>
::Animation1DProc_RandomTwoColors(const AnimProcColorType& colorOff, const AnimProcColorType& colorOn, uint8_t density)
: Animation1DProcType(_colorGenInst,  density)
, _colorGenInst      (colorOff,       colorOn)
{}


template <typename ColorAnimator1D_T, bool SkipDrawingClearPixel, typename AnimProcColorT, uint8_t AnimChannelIndexes>
inline Animation1DProc_RandomTwoColors<ColorAnimator1D_T, SkipDrawingClearPixel, AnimProcColorT, AnimChannelIndexes>
::~Animation1DProc_RandomTwoColors()
{}


} // namespace eCxx


#endif
