/*
 * 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_BUFFER_STORAGE_POLICY_H
#define ECXX_CTL_GFX_COLOR_BUFFER_STORAGE_POLICY_H


namespace eCxx {


//
// Color buffer storage policy constants
//
namespace CBP {

    struct RawBuffer;          // Array of uint8_t
    struct EncapsulatedBuffer; // Array of ColorT

} // namespace CBP


//
// Color buffer storage policy structures
//
template <typename BufferColorT, typename BufferSizeT, CBP::DUInt BufferSizeN, typename BufferPolicyT>
struct ColorBufferStoragePolicy;


template <typename BufferColorT, typename BufferSizeT, CBP::DUInt BufferSizeN>
struct ColorBufferStoragePolicy<BufferColorT, BufferSizeT, BufferSizeN, CBP::RawBuffer> : public Uninstantiable {
    using BufferColorType    = BufferColorT;
    using BufferSizeType     = BufferSizeT;
    using BufferByteSizeType = CBP::DUInt;

    using BufferArrayType    = uint8_t[BufferSizeN * BufferColorT::RawByteSize];
    using Iterator           = uint8_t*;
    using ConstIterator      = const uint8_t*;

    static constexpr uint8_t            ColorByteSize  = BufferColorT::RawByteSize;

    static constexpr BufferSizeType     BufferSize     = BufferSizeN;
    static constexpr BufferByteSizeType BufferByteSize = BufferSizeN * BufferColorT::RawByteSize;

    static inline const BufferColorType get(const uint8_t* buff, BufferSizeType index)
    {
        BufferColorType c;
        ConstIterator   it = buff + ColorByteSize * index;
                              {       SelectColorChannel<BufferColorType, CCI::_1>::_(c) = *it; }
        if(ColorByteSize > 1) { ++it; SelectColorChannel<BufferColorType, CCI::_2>::_(c) = *it; }
        if(ColorByteSize > 2) { ++it; SelectColorChannel<BufferColorType, CCI::_3>::_(c) = *it; }
        if(ColorByteSize > 3) { ++it; SelectColorChannel<BufferColorType, CCI::_4>::_(c) = *it; }

        return c;
    }

    static inline Iterator itBegin(uint8_t* buff)
    { return buff; }

    static inline Iterator itEnd(uint8_t* buff)
    { return buff + BufferByteSize; }

    static inline ConstIterator itCBegin(const uint8_t* buff)
    { return buff; }

    static inline ConstIterator itCEnd(const uint8_t* buff)
    { return buff + BufferByteSize; }

    static inline void itInc(Iterator& it)
    { it += ColorByteSize; }

    static inline void itInc(Iterator& it, BufferSizeType amount)
    { it += (ColorByteSize * amount); }

    static inline void itAssign(Iterator it, const BufferColorType& color)
    {
        BufferColorType::preXPolicy(it);

                              *(it + 0) = SelectColorChannel<BufferColorType, CCI::_1>::_(color);
        if(ColorByteSize > 1) *(it + 1) = SelectColorChannel<BufferColorType, CCI::_2>::_(color);
        if(ColorByteSize > 2) *(it + 2) = SelectColorChannel<BufferColorType, CCI::_3>::_(color);
        if(ColorByteSize > 3) *(it + 3) = SelectColorChannel<BufferColorType, CCI::_4>::_(color);

        BufferColorType::postXPolicy(it);
    }

    static inline void itAssignInc(Iterator& it, const BufferColorType& color)
    {
        BufferColorType::preXPolicy(it);

                              *it++ = SelectColorChannel<BufferColorType, CCI::_1>::_(color);
        if(ColorByteSize > 1) *it++ = SelectColorChannel<BufferColorType, CCI::_2>::_(color);
        if(ColorByteSize > 2) *it++ = SelectColorChannel<BufferColorType, CCI::_3>::_(color);
        if(ColorByteSize > 3) *it++ = SelectColorChannel<BufferColorType, CCI::_4>::_(color);

        BufferColorType::postXPolicy(it - ColorByteSize);
    }

    template <uint8_t ChannelIndexes>
    static inline void itAdjustInc(Iterator& it, int8_t adjustmentFactor)
    {
        if(ChannelIndexes == BufferColorType::A) {
            itAdjustInc(it, adjustmentFactor);
            return;
        }

        BufferColorType::preXPolicy(it);

                              { if(ChannelIndexes & CCI::_1) *it = eCxx::adjustColor<BufferColorType::MaxC0Value>(*it, adjustmentFactor); ++it; }
        if(ColorByteSize > 1) { if(ChannelIndexes & CCI::_2) *it = eCxx::adjustColor<BufferColorType::MaxC1Value>(*it, adjustmentFactor); ++it; }
        if(ColorByteSize > 2) { if(ChannelIndexes & CCI::_3) *it = eCxx::adjustColor<BufferColorType::MaxC2Value>(*it, adjustmentFactor); ++it; }
        if(ColorByteSize > 3) { if(ChannelIndexes & CCI::_4) *it = eCxx::adjustColor<BufferColorType::MaxC3Value>(*it, adjustmentFactor); ++it; }

        BufferColorType::postXPolicy(it - ColorByteSize);
    }

    static inline void itAdjustInc(Iterator& it, int8_t adjustmentFactor)
    {
        BufferColorType::preXPolicy(it);

                              { *it = eCxx::adjustColor<BufferColorType::MaxC0Value>(*it, adjustmentFactor); ++it; }
        if(ColorByteSize > 1) { *it = eCxx::adjustColor<BufferColorType::MaxC1Value>(*it, adjustmentFactor); ++it; }
        if(ColorByteSize > 2) { *it = eCxx::adjustColor<BufferColorType::MaxC2Value>(*it, adjustmentFactor); ++it; }
        if(ColorByteSize > 3) { *it = eCxx::adjustColor<BufferColorType::MaxC3Value>(*it, adjustmentFactor); ++it; }

        BufferColorType::postXPolicy(it - ColorByteSize);
    }

    template <uint8_t ChannelIndexes>
    static inline void itAdjustInc(Iterator& it, int16_t adjustmentFactor)
    {
        if(ChannelIndexes == BufferColorType::A) {
            itAdjustInc(it, adjustmentFactor);
            return;
        }

        BufferColorType::preXPolicy(it);

                              { if(ChannelIndexes & CCI::_1) *it = eCxx::adjustColor16<BufferColorType::MaxC0Value>(*it, adjustmentFactor); ++it; }
        if(ColorByteSize > 1) { if(ChannelIndexes & CCI::_2) *it = eCxx::adjustColor16<BufferColorType::MaxC1Value>(*it, adjustmentFactor); ++it; }
        if(ColorByteSize > 2) { if(ChannelIndexes & CCI::_3) *it = eCxx::adjustColor16<BufferColorType::MaxC2Value>(*it, adjustmentFactor); ++it; }
        if(ColorByteSize > 3) { if(ChannelIndexes & CCI::_4) *it = eCxx::adjustColor16<BufferColorType::MaxC3Value>(*it, adjustmentFactor); ++it; }

        BufferColorType::postXPolicy(it - ColorByteSize);
    }

    static inline void itAdjustInc(Iterator& it, int16_t adjustmentFactor)
    {
        BufferColorType::preXPolicy(it);

                              { *it = eCxx::adjustColor16<BufferColorType::MaxC0Value>(*it, adjustmentFactor); ++it; }
        if(ColorByteSize > 1) { *it = eCxx::adjustColor16<BufferColorType::MaxC1Value>(*it, adjustmentFactor); ++it; }
        if(ColorByteSize > 2) { *it = eCxx::adjustColor16<BufferColorType::MaxC2Value>(*it, adjustmentFactor); ++it; }
        if(ColorByteSize > 3) { *it = eCxx::adjustColor16<BufferColorType::MaxC3Value>(*it, adjustmentFactor); ++it; }

        BufferColorType::postXPolicy(it - ColorByteSize);
    }

    template <uint8_t ChannelIndexes>
    static inline void itScaleInc(Iterator& it, uint8_t scaleFactor)
    {
        if(ChannelIndexes == BufferColorType::A) {
            itScaleInc(it, scaleFactor);
            return;
        }

        BufferColorType::preXPolicy(it);

                              { if(ChannelIndexes & CCI::_1) *it = eCxx::scaleColor<BufferColorType::MaxC0Value>(*it, scaleFactor); ++it; }
        if(ColorByteSize > 1) { if(ChannelIndexes & CCI::_2) *it = eCxx::scaleColor<BufferColorType::MaxC1Value>(*it, scaleFactor); ++it; }
        if(ColorByteSize > 2) { if(ChannelIndexes & CCI::_3) *it = eCxx::scaleColor<BufferColorType::MaxC2Value>(*it, scaleFactor); ++it; }
        if(ColorByteSize > 3) { if(ChannelIndexes & CCI::_4) *it = eCxx::scaleColor<BufferColorType::MaxC3Value>(*it, scaleFactor); ++it; }

        BufferColorType::postXPolicy(it - ColorByteSize);
    }

    static inline void itScaleInc(Iterator& it, uint8_t scaleFactor)
    {
        BufferColorType::preXPolicy(it);

                              { *it = eCxx::scaleColor<BufferColorType::MaxC0Value>(*it, scaleFactor); ++it; }
        if(ColorByteSize > 1) { *it = eCxx::scaleColor<BufferColorType::MaxC1Value>(*it, scaleFactor); ++it; }
        if(ColorByteSize > 2) { *it = eCxx::scaleColor<BufferColorType::MaxC2Value>(*it, scaleFactor); ++it; }
        if(ColorByteSize > 3) { *it = eCxx::scaleColor<BufferColorType::MaxC3Value>(*it, scaleFactor); ++it; }

        BufferColorType::postXPolicy(it - ColorByteSize);
    }

    template <uint8_t N>
    static inline void itCIE1931LCCInc(Iterator& it, CIE1931LVal (&lut)[N])
    {
        BufferColorType::preXPolicy(it);

                              { *it = eCxx::cie1931lcc(lut, *it); ++it; }
        if(ColorByteSize > 1) { *it = eCxx::cie1931lcc(lut, *it); ++it; }
        if(ColorByteSize > 2) { *it = eCxx::cie1931lcc(lut, *it); ++it; }
        if(ColorByteSize > 3) { *it = eCxx::cie1931lcc(lut, *it); ++it; }

        BufferColorType::postXPolicy(it - ColorByteSize);
    }

    template <uint8_t N>
    static inline void itCIE1931LUTInc(Iterator& it, CIE1931LVal (&lut)[N])
    {
        BufferColorType::preXPolicy(it);

                              { *it = eCxx::cie1931lut(lut, *it); ++it; }
        if(ColorByteSize > 1) { *it = eCxx::cie1931lut(lut, *it); ++it; }
        if(ColorByteSize > 2) { *it = eCxx::cie1931lut(lut, *it); ++it; }
        if(ColorByteSize > 3) { *it = eCxx::cie1931lut(lut, *it); ++it; }

        BufferColorType::postXPolicy(it - ColorByteSize);
    }
};


template <typename BufferColorT, typename BufferSizeT, CBP::DUInt BufferSizeN>
struct ColorBufferStoragePolicy<BufferColorT, BufferSizeT, BufferSizeN, CBP::EncapsulatedBuffer> : public Uninstantiable {
    using BufferColorType    = BufferColorT;
    using BufferSizeType     = BufferSizeT;
    using BufferByteSizeType = CBP::DUInt;

    using BufferArrayType    = BufferColorT[BufferSizeN];
    using Iterator           = BufferColorT*;
    using ConstIterator      = const BufferColorT*;

    static constexpr uint8_t            ColorByteSize  = BufferColorT::RawByteSize;

    static constexpr BufferSizeType     BufferSize     = BufferSizeN;
    static constexpr BufferByteSizeType BufferByteSize = BufferSizeN * BufferColorT::RawByteSize;

    static inline const BufferColorType get(const BufferColorType* buff, BufferSizeType index)
    { return buff[index]; }

    static inline Iterator itBegin(BufferColorType* buff)
    { return buff; }

    static inline Iterator itEnd(BufferColorType* buff)
    { return buff + BufferSize; }

    static inline ConstIterator itCBegin(const BufferColorType* buff)
    { return buff; }

    static inline ConstIterator itCEnd(const BufferColorType* buff)
    { return buff + BufferSize; }

    static inline void itInc(Iterator& it)
    { ++it; }

    static inline void itInc(Iterator& it, BufferSizeType amount)
    { it += amount; }

    static inline void itAssign(Iterator it, const BufferColorType& color)
    { *it = color; }

    static inline void itAssignInc(Iterator& it, const BufferColorType& color)
    { *it++ = color; }

    template <uint8_t ChannelIndexes>
    static inline void itAdjustInc(Iterator& it, int8_t adjustmentFactor)
    {
        if(ChannelIndexes == BufferColorType::A) {
            itAdjustInc(it, adjustmentFactor);
            return;
        }

        it->template adjust<ChannelIndexes>(adjustmentFactor);
        ++it;
    }

    static inline void itAdjustInc(Iterator& it, int8_t adjustmentFactor)
    {
        it->adjust(adjustmentFactor);
        ++it;
    }

    template <uint8_t ChannelIndexes>
    static inline void itAdjustInc(Iterator& it, int16_t adjustmentFactor)
    {
        if(ChannelIndexes == BufferColorType::A) {
            itAdjustInc(it, adjustmentFactor);
            return;
        }

        it->template adjust16<ChannelIndexes>(adjustmentFactor);
        ++it;
    }

    static inline void itAdjustInc(Iterator& it, int16_t adjustmentFactor)
    {
        it->adjust16(adjustmentFactor);
        ++it;
    }

    template <uint8_t ChannelIndexes>
    static inline void itScaleInc(Iterator& it, uint8_t scaleFactor)
    {
        if(ChannelIndexes == BufferColorType::A) {
            itScaleInc(it, scaleFactor);
            return;
        }

        it->template scale<ChannelIndexes>(scaleFactor);
        ++it;
    }

    static inline void itScaleInc(Iterator& it, uint8_t scaleFactor)
    {
        it->scale(scaleFactor);
        ++it;
    }

    template <uint8_t N>
    static inline void itCIE1931LCCInc(Iterator& it, CIE1931LVal (&lut)[N])
    {
        it->cie1931lcc(lut);
        ++it;
    }

    template <uint8_t N>
    static inline void itCIE1931LUTInc(Iterator& it, CIE1931LVal (&lut)[N])
    {
        it->cie1931lut(lut);
        ++it;
    }
};


} // namespace eCxx


#endif
