/*
 * 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_1D_TPP
#error "this file cannot be included directly"
#endif
#undef INCLUDE_ECXX_CTL_GFX_COLOR_ANIMATOR_1D_TPP


#ifndef ECXX_CTL_GFX_COLOR_ANIMATOR_1D_TPP
#define ECXX_CTL_GFX_COLOR_ANIMATOR_1D_TPP


namespace eCxx {


//
// The interface class for all simple 1-dimensional animation animation processor classes
//
template <typename ColorFX1D_T>
inline Animation1DProc_Interface<ColorFX1D_T>::~Animation1DProc_Interface()
{}


//
// A simple 1-dimensional color animator class
//
template <uint8_t SegmentCountN, typename ColorFX1D_T>
inline ColorAnimator1D<SegmentCountN, ColorFX1D_T>
::ColorAnimator1D(ColorFX1DType& colorFX1D)
: _colorFX1D(&colorFX1D)
{}


template <uint8_t SegmentCountN, typename ColorFX1D_T>
void ColorAnimator1D<SegmentCountN, ColorFX1D_T>
::initSegment(uint8_t segmentIdx, BufferSizeType elemBeginIdx, BufferSizeType elemEndIdx, uint8_t animDelay, Animation1DProcType& animProc)
{
    // Check the parameters
    if(segmentIdx   >= SegmentCount      ) return;
    if(elemBeginIdx >= BufferElementCount) return;
    if(elemEndIdx   >= BufferElementCount) return;
    if(elemBeginIdx >  elemEndIdx        ) return;

    // Save the parameters
    _segmentDef[segmentIdx].begin     = elemBeginIdx;
    _segmentDef[segmentIdx].end       = elemEndIdx;
    _segmentDef[segmentIdx].animDelay = (animDelay <= 250) ? animDelay : 250;
    _segmentDef[segmentIdx].animCtr   = 0;
    _segmentDef[segmentIdx].animProc  = &animProc;
}


template <uint8_t SegmentCountN, typename ColorFX1D_T>
void ColorAnimator1D<SegmentCountN, ColorFX1D_T>
::initSegment(uint8_t animDelay, Animation1DProcType& animProc)
{ if(SegmentCount == 1) initSegment(0, 0, BufferElementCount - 1, animDelay, animProc); }


template <uint8_t SegmentCountN, typename ColorFX1D_T>
inline void ColorAnimator1D<SegmentCountN, ColorFX1D_T>
::reset()
{
    for(uint8_t i = 0; i < SegmentCount; ++i) {
        _segmentDef[i].animCtr = 0;
        if(_segmentDef[i].animProc) {
            _segmentDef[i].animProc->reset(*_colorFX1D, _segmentDef[i].begin, _segmentDef[i].end);
        }
    }

    _colorFX1D->clear();
}


template <uint8_t SegmentCountN, typename ColorFX1D_T>
inline void ColorAnimator1D<SegmentCountN, ColorFX1D_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(*_colorFX1D, _segmentDef[i].begin, _segmentDef[i].end);
            }
        }

    }
}


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


template <uint8_t SegmentCountN, typename ColorFX1D_T>
inline CBP::QUInt ColorAnimator1D<SegmentCountN, ColorFX1D_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 ColorFX1D_T>
inline CBP::QUInt ColorAnimator1D<SegmentCountN, ColorFX1D_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
