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


#ifndef INCLUDE_ECXX_CTL_GFX_COLOR_ANIMATOR_2D_TPP
#error "this file cannot be included directly"
#endif
#undef INCLUDE_ECXX_CTL_GFX_COLOR_ANIMATOR_2D_TPP


#ifndef ECXX_CTL_GFX_COLOR_ANIMATOR_2D_TPP
#define ECXX_CTL_GFX_COLOR_ANIMATOR_2D_TPP


namespace eCxx {


//
// The interface class for all simple 2-dimensional animation animation processor classes
//
template <typename ColorFX2D_T>
inline Animation2DProc_Interface<ColorFX2D_T>::~Animation2DProc_Interface()
{}


//
// A simple 2-dimensional color animator class
//
template <uint8_t SegmentCountN, typename ColorFX2D_T>
inline ColorAnimator2D<SegmentCountN, ColorFX2D_T>
::ColorAnimator2D(ColorFX2DType& colorFX2D)
: _colorFX2D(&colorFX2D)
{}


template <uint8_t SegmentCountN, typename ColorFX2D_T>
void ColorAnimator2D<SegmentCountN, ColorFX2D_T>
::initSegment(uint8_t segmentIdx, BufferSizeType elemXBeginIdx, BufferSizeType elemYBeginIdx, BufferSizeType elemXEndIdx, BufferSizeType elemYEndIdx, uint8_t animDelay, Animation2DProcType& animProc)
{
    // Check the parameters
    if(segmentIdx    >= SegmentCount ) return;

    if(elemXBeginIdx >= XElementCount) return;
    if(elemXEndIdx   >= XElementCount) return;
    if(elemXBeginIdx >  elemXEndIdx  ) return;

    if(elemYBeginIdx >= YElementCount) return;
    if(elemYEndIdx   >= YElementCount) return;
    if(elemYBeginIdx >  elemYEndIdx  ) return;

    // Save the parameters
    _segmentDef[segmentIdx].beginX    = elemXBeginIdx;
    _segmentDef[segmentIdx].beginY    = elemYBeginIdx;
    _segmentDef[segmentIdx].endX      = elemXEndIdx;
    _segmentDef[segmentIdx].endY      = elemYEndIdx;
    _segmentDef[segmentIdx].animDelay = (animDelay <= 250) ? animDelay : 250;
    _segmentDef[segmentIdx].animCtr   = 0;
    _segmentDef[segmentIdx].animProc  = &animProc;
}


template <uint8_t SegmentCountN, typename ColorFX2D_T>
void ColorAnimator2D<SegmentCountN, ColorFX2D_T>
::initSegment(uint8_t animDelay, Animation2DProcType& animProc)
{ if(SegmentCount == 1) initSegment(0, 0, 0, XElementCount - 1, YElementCount - 1, animDelay, animProc); }


template <uint8_t SegmentCountN, typename ColorFX2D_T>
inline void ColorAnimator2D<SegmentCountN, ColorFX2D_T>
::reset()
{
    for(uint8_t i = 0; i < SegmentCount; ++i) {
        _segmentDef[i].animCtr = 0;
        if(_segmentDef[i].animProc) {
            _segmentDef[i].animProc->reset(*_colorFX2D, _segmentDef[i].beginX, _segmentDef[i].beginY, _segmentDef[i].endX, _segmentDef[i].endY);
        }
    }

    _colorFX2D->clear();
}


template <uint8_t SegmentCountN, typename ColorFX2D_T>
inline void ColorAnimator2D<SegmentCountN, ColorFX2D_T>
::update()
{
    for(uint8_t i = 0; i < SegmentCount; ++i) {
        if(_segmentDef[i].animCtr) {
            --_segmentDef[i].animCtr;
        }
        else {
            _segmentDef[i].animCtr = _segmentDef[i].animDelay;
            if(_segmentDef[i].animProc) {
                _segmentDef[i].animProc->update(*_colorFX2D, _segmentDef[i].beginX, _segmentDef[i].beginY, _segmentDef[i].endX, _segmentDef[i].endY);
            }
        }

    }
}


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


template <uint8_t SegmentCountN, typename ColorFX2D_T>
inline CBP::QUInt ColorAnimator2D<SegmentCountN, ColorFX2D_T>
::lcmOfAllAnimationDelay() const
{
    CBP::QUInt result = _segmentDef[0].animDelay + 1;

    for(uint8_t i = 1; i < SegmentCount; ++i) {
        result = lcm<CBP::QUInt>( result, _segmentDef[i].animDelay + 1);
    }

    return result;
}


template <uint8_t SegmentCountN, typename ColorFX2D_T>
inline CBP::QUInt ColorAnimator2D<SegmentCountN, ColorFX2D_T>
::estimatedTotalAnimationFrameCount() const
{
    CBP::QUInt result = _segmentDef[0].animProc ? ( (_segmentDef[0].animDelay + 1)
                                                    *
                                                    _segmentDef[0].animProc->estimatedTotalAnimationFrameCount()
                                                  )
                                                : 1;

    for(uint8_t i = 1; i < SegmentCount; ++i) {
        if(!_segmentDef[i].animProc) continue;
        result = lcm<CBP::QUInt>(
            result,
            (_segmentDef[i].animDelay + 1) * _segmentDef[i].animProc->estimatedTotalAnimationFrameCount()
        );
    }

    return result;
}


#endif


} // namespace eCxx


#endif
