/*
 * 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_ANIMATOR_2D_H
#define ECXX_CTL_GFX_COLOR_ANIMATOR_2D_H


#include "ColorFX2D.h"
#include "ColorGenerator2D.h"


namespace eCxx {


//
// The interface class for all simple 2-dimensional animation processor classes
//
// NOTE : Some animation processor implementations may need color correction (via CIE-1931 lightness)
//        to be manually applied using the ColorFX2D<T> class' functionality to make the result visually
//        better.
//
template <typename ColorFX2D_T>
class Animation2DProc_Interface {
    public:
        using ColorFX2DType   = ColorFX2D_T;
        using BufferColorType = typename ColorFX2D_T::BufferColorType;
        using BufferSizeType  = typename ColorFX2D_T::BufferSizeType;

    public:
        inline Animation2DProc_Interface(const Animation2DProc_Interface&) = delete;
        inline Animation2DProc_Interface(Animation2DProc_Interface&&) = default;

        inline Animation2DProc_Interface& operator=(const Animation2DProc_Interface&) = delete;
        inline Animation2DProc_Interface& operator=(Animation2DProc_Interface&&) = delete;

    protected:
        inline Animation2DProc_Interface() = default;

    public:
        inline virtual ~Animation2DProc_Interface() = 0;

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

#if defined(__x86_64__) || defined(__i386__)
        // NOTE : Some animation processor implementations may require reset() to be
        //        called first for this function to work properly
        virtual CBP::QUInt estimatedTotalAnimationFrameCount() const = 0;
#endif
};


//
// A simple 2-dimensional color animator class
//
template <uint8_t SegmentCountN, typename ColorFX2D_T>
class ColorAnimator2D {
    static_assert(
        (SegmentCountN >= 1) && (SegmentCountN <= 16),
        "template argument substituting SegmentCountN is not between 1 and 16"
    );

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

    private:
        // Check whether ColorFX2D_T is really a ColorFX2D<T>
        using IsColorFX2D = typename ColorFX2D_T::__ColorFX2D__;

    public:
        using ColorFX2DType       = ColorFX2D_T;
        using Animation2DProcType = Animation2DProc_Interface<ColorFX2D_T>;

        using BufferColorType = typename ColorFX2D_T::BufferColorType;
        using BufferSizeType  = typename ColorFX2D_T::BufferSizeType;

        static constexpr uint8_t        ColorByteSize      = ColorFX2D_T::ColorByteSize;
        static constexpr BufferSizeType BufferElementCount = ColorFX2D_T::BufferElementCount;

        static constexpr CBP::SUInt     XElementCount      = ColorFX2D_T::XElementCount;
        static constexpr CBP::SUInt     YElementCount      = ColorFX2D_T::YElementCount;

        static constexpr uint8_t        SegmentCount       = SegmentCountN;

    public:
        inline ColorAnimator2D() = delete;
        inline ~ColorAnimator2D() = default;

        inline ColorAnimator2D(const ColorAnimator2D&) = delete;
        inline ColorAnimator2D(ColorAnimator2D&&) = default;

        inline ColorAnimator2D& operator=(const ColorAnimator2D&) = delete;
        inline ColorAnimator2D& operator=(ColorAnimator2D&&) = delete;

    public:
        inline ColorAnimator2D(ColorFX2DType& colorFX2D);

        // Initialize a segment
        // (the animation delay must be between 0 and 250)
        inline void initSegment(uint8_t segmentIdx, BufferSizeType elemXBeginIdx, BufferSizeType elemYBeginIdx, BufferSizeType elemXEndIdx, BufferSizeType elemYEndIdx, uint8_t animDelay, Animation2DProcType& animProc);

        // A simplified version initSegment() that can be called if there is only one segment
        // (the animation delay must be between 0 and 250)
        inline void initSegment(uint8_t animDelay, Animation2DProcType& animProc);

        inline void reset();
        inline void update();

#if defined(__x86_64__) || defined(__i386__)
        inline CBP::QUInt lcmOfAllAnimationDelay() const;
        inline CBP::QUInt estimatedTotalAnimationFrameCount() const;
#endif

    private:
        struct SegmentDef {
            BufferSizeType       beginX    = 0;                 // First X element index
            BufferSizeType       beginY    = 0;                 // First Y element index
            BufferSizeType       endX      = XElementCount - 1; // Last  X element index
            BufferSizeType       endY      = YElementCount - 1; // Last  Y element index
            uint8_t              animDelay = 0;                 // Animation delay value
            uint8_t              animCtr   = 0;                 // Animation delay counter
            Animation2DProcType* animProc  = nullptr;           // Animation animation processor instance
        };

    private:
        ColorFX2DType* const _colorFX2D;
        SegmentDef           _segmentDef[SegmentCount];
};


//
// makeColorAnimator2D<T>()
//
template <uint8_t SegmentCountN, typename ColorFX2D_T>
static inline __ECXX_ATTR_ALWAYS_INLINE ColorAnimator2D<SegmentCountN, ColorFX2D_T> makeColorAnimator2D(ColorFX2D_T& colorFX2D)
{ return ColorAnimator2D<SegmentCountN, ColorFX2D_T>(colorFX2D); }


} // namespace eCxx


//
// Include the implementation file
//
#define INCLUDE_ECXX_CTL_GFX_COLOR_ANIMATOR_2D_TPP
#include "ColorAnimator2D.tpp"


#endif
