/*
 * 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_1D_H
#define ECXX_CTL_GFX_COLOR_ANIMATOR_1D_H


#include "ColorFX1D.h"
#include "ColorGenerator1D.h"


namespace eCxx {


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

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

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

    protected:
        inline Animation1DProc_Interface() = default;

    public:
        inline virtual ~Animation1DProc_Interface() = 0;

        virtual void reset(ColorFX1DType& colorFX1D, BufferSizeType elemBeginIdx, BufferSizeType elemEndIdx) = 0;
        virtual void update(ColorFX1DType& colorFX1D, BufferSizeType elemBeginIdx, BufferSizeType elemEndIdx) = 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 1-dimensional color animator class
//
template <uint8_t SegmentCountN, typename ColorFX1D_T>
class ColorAnimator1D {
    static_assert(
        (SegmentCountN >= 1) && (SegmentCountN <= 16),
        "template argument substituting SegmentCountN is not between 1 and 16"
    );

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

    private:
        // Check whether ColorFX1D_T is really a ColorFX1D<T>
        using IsColorFX1D = typename ColorFX1D_T::__ColorFX1D__;

    public:
        using ColorFX1DType       = ColorFX1D_T;
        using Animation1DProcType = Animation1DProc_Interface<ColorFX1D_T>;

        using BufferColorType = typename ColorFX1D_T::BufferColorType;
        using BufferSizeType  = typename ColorFX1D_T::BufferSizeType;

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

        static constexpr uint8_t        SegmentCount       = SegmentCountN;

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

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

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

    public:
        inline ColorAnimator1D(ColorFX1DType& colorFX1D);

        // Initialize a segment
        // (the animation delay must be between 0 and 250)
        inline void initSegment(uint8_t segmentIdx, BufferSizeType elemBeginIdx, BufferSizeType elemEndIdx, uint8_t animDelay, Animation1DProcType& 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, Animation1DProcType& 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       begin     = 0;                      // First element index
            BufferSizeType       end       = BufferElementCount - 1; // Last  element index
            uint8_t              animDelay = 0;                      // Animation delay value
            uint8_t              animCtr   = 0;                      // Animation delay counter
            Animation1DProcType* animProc  = nullptr;                // Animation animation processor instance
        };

    private:
        ColorFX1DType* const _colorFX1D;
        SegmentDef           _segmentDef[SegmentCount];
};


//
// makeColorAnimator1D<T>()
//
template <uint8_t SegmentCountN, typename ColorFX1D_T>
static inline __ECXX_ATTR_ALWAYS_INLINE ColorAnimator1D<SegmentCountN, ColorFX1D_T> makeColorAnimator1D(ColorFX1D_T& colorFX1D)
{ return ColorAnimator1D<SegmentCountN, ColorFX1D_T>(colorFX1D); }


} // namespace eCxx


//
// Include the implementation file
//
#define INCLUDE_ECXX_CTL_GFX_COLOR_ANIMATOR_1D_TPP
#include "ColorAnimator1D.tpp"


#endif
