/*
 * 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_2D_PROC_RANDOM_H
#define ECXX_CTL_GFX_COLOR_ANIMATION_2D_PROC_RANDOM_H


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


namespace eCxx {


//
// A simple 2-dimensional animation processor class - random color(s) using ColorGenerator2D<T>
//
// NOTE : If SkipDrawingClearPixel is set to true, the corresponding ColorFX2D<T> instance
//        will need to be cleared manually, either by calling its clear() method or filling
//        its buffer via another methods.
//
template <
    typename ColorAnimator2D_T,
    typename ColorGeneratorImplementationT,
    bool     SkipDrawingClearPixel = false,
    typename AnimProcColorT        = typename ColorAnimator2D_T::ColorFX2DType::BufferColorType
>
class Animation2DProc_Random : public ColorAnimator2D_T::Animation2DProcType {
    public:
        using AnimProcColorType    = AnimProcColorT;
        using ColorAnimator2DType  = ColorAnimator2D_T;
        using ColorFX2DType        = typename ColorAnimator2D_T::Animation2DProcType::ColorFX2DType;
        using BufferColorType      = typename ColorAnimator2D_T::Animation2DProcType::BufferColorType;
        using BufferSizeType       = typename ColorAnimator2D_T::Animation2DProcType::BufferSizeType;

        using ColorGenerator2DType = ColorGenerator2D_CRTPBase<ColorGeneratorImplementationT, AnimProcColorType, BufferSizeType>;

    public:
        inline Animation2DProc_Random(ColorGenerator2DType& colorGen, uint8_t density);
        inline virtual ~Animation2DProc_Random();

        virtual void reset(ColorFX2DType& colorFX2D, BufferSizeType elemXBeginIdx, BufferSizeType elemYBeginIdx, BufferSizeType elemXEndIdx, BufferSizeType elemYEndIdx);
        virtual void update(ColorFX2DType& colorFX2D, BufferSizeType elemXBeginIdx, BufferSizeType elemYBeginIdx, BufferSizeType elemXEndIdx, BufferSizeType elemYEndIdx);

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


template <typename ColorAnimator2D_T, typename ColorGeneratorImplementationT, bool SkipDrawingClearPixel, typename AnimProcColorT>
inline Animation2DProc_Random<ColorAnimator2D_T, ColorGeneratorImplementationT, SkipDrawingClearPixel, AnimProcColorT>
::Animation2DProc_Random(ColorGenerator2DType& colorGen, uint8_t density)
: _colorGen(&colorGen)
, _density ( density )
{}


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


template <typename ColorAnimator2D_T, typename ColorGeneratorImplementationT, bool SkipDrawingClearPixel, typename AnimProcColorT>
void Animation2DProc_Random<ColorAnimator2D_T, ColorGeneratorImplementationT, SkipDrawingClearPixel, AnimProcColorT>
::reset(ColorFX2DType&, BufferSizeType, BufferSizeType, BufferSizeType, BufferSizeType)
{ _colorGen->reset(); }


template <typename ColorAnimator2D_T, typename ColorGeneratorImplementationT, bool SkipDrawingClearPixel, typename AnimProcColorT>
void Animation2DProc_Random<ColorAnimator2D_T, ColorGeneratorImplementationT, SkipDrawingClearPixel, AnimProcColorT>
::update(ColorFX2DType& colorFX2D, BufferSizeType elemXBeginIdx, BufferSizeType elemYBeginIdx, BufferSizeType elemXEndIdx, BufferSizeType elemYEndIdx)
{
    // Set the color(s)
    for(BufferSizeType y = elemYBeginIdx; y <= elemYEndIdx; ++y) {
        for(BufferSizeType x = elemXBeginIdx; x <= elemXEndIdx; ++x) {
            // If configured, skip drawing clear pixel
            __if_constexpr(SkipDrawingClearPixel) {
                if(Application::random8() <= _density) {
                    colorFX2D.setXY(
                        x, y,
                        _colorGen->generate(elemXBeginIdx, elemYBeginIdx, elemXEndIdx, elemYEndIdx, x, y, true)
                    );
                }
            }
            // Draw normally
            else {
                colorFX2D.setXY(
                    x, y,
                    _colorGen->generate(
                        elemXBeginIdx, elemYBeginIdx, elemXEndIdx, elemYEndIdx, x, y, Application::random8() <= _density
                    )
                );
            }
        }
    }
}


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


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

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

#endif


//
// A simple 2-dimensional animation processor class - random between off and on colors
//
// NOTE : If SkipDrawingClearPixel is set to true, the corresponding ColorFX2D<T> instance
//        will need to be cleared manually, either by calling its clear() method or filling
//        its buffer via another methods.
//
template <
    typename ColorAnimator2D_T,
    bool     SkipDrawingClearPixel = false,
    typename AnimProcColorT        = typename ColorAnimator2D_T::ColorFX2DType::BufferColorType,
    uint8_t  AnimChannelIndexes    = AnimProcColorT::A
>
class Animation2DProc_RandomTwoColors : public Animation2DProc_Random<
    ColorAnimator2D_T,
    ColorGenerator2D_OffOn<AnimProcColorT, typename ColorAnimator2D_T::Animation2DProcType::BufferSizeType>,
    SkipDrawingClearPixel,
    AnimProcColorT
> {
    public:
        using Animation2DProcType      = Animation2DProc_Random<
                                             ColorAnimator2D_T,
                                             ColorGenerator2D_OffOn<
                                                 AnimProcColorT,
                                                 typename ColorAnimator2D_T::Animation2DProcType::BufferSizeType
                                             >,
                                             SkipDrawingClearPixel,
                                             AnimProcColorT
                                         >;

        using ColorGenerator2DImplType = ColorGenerator2D_OffOn<
                                             AnimProcColorT,
                                             typename ColorAnimator2D_T::Animation2DProcType::BufferSizeType,
                                             AnimChannelIndexes
                                         >;

        using AnimProcColorType    = typename Animation2DProcType::AnimProcColorType;
        using ColorAnimator2DType  = typename Animation2DProcType::ColorAnimator2DType;
        using ColorFX2DType        = typename Animation2DProcType::ColorFX2DType;
        using BufferColorType      = typename Animation2DProcType::BufferColorType;
        using BufferSizeType       = typename Animation2DProcType::BufferSizeType;

        using ColorGenerator2DType = typename Animation2DProcType::ColorGenerator2DType;

    public:
        inline Animation2DProc_RandomTwoColors(const AnimProcColorType& colorOff, const AnimProcColorType& colorOn, uint8_t density);
        inline virtual ~Animation2DProc_RandomTwoColors();

    private:
        ColorGenerator2DImplType _colorGenInst;
};


template <typename ColorAnimator2D_T, bool SkipDrawingClearPixel, typename AnimProcColorT, uint8_t AnimChannelIndexes>
inline Animation2DProc_RandomTwoColors<ColorAnimator2D_T, SkipDrawingClearPixel, AnimProcColorT, AnimChannelIndexes>
::Animation2DProc_RandomTwoColors(const AnimProcColorType& colorOff, const AnimProcColorType& colorOn, uint8_t density)
: Animation2DProcType(_colorGenInst,  density)
, _colorGenInst      (colorOff,       colorOn)
{}


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


} // namespace eCxx


#endif
