/*
 * 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_UTILITY_H
#define ECXX_CTL_GFX_COLOR_UTILITY_H


#include <eCxx/CTL/MathUtility.h>


namespace eCxx {


//
// Color channel indexes
//
namespace CCI {


// Color channel indexes
static constexpr uint8_t _A = 0b11111111; // Denotes all channel when used in lerpColor<ChannelIndex, ColorT>()

static constexpr uint8_t _1 = 0b00000001;
static constexpr uint8_t _2 = 0b00000010;
static constexpr uint8_t _3 = 0b00000100;
static constexpr uint8_t _4 = 0b00001000;


// Helper function to convert singular (non-masked) color channel index ID to plain channel index
template <uint8_t ChannelIndex>
struct toPlainIndex { static_assert(ChannelIndex, "is a non-singular (masked) color channel ID"); };

template <> struct toPlainIndex<_1> { static constexpr uint8_t Value = 0; };
template <> struct toPlainIndex<_2> { static constexpr uint8_t Value = 1; };
template <> struct toPlainIndex<_3> { static constexpr uint8_t Value = 2; };
template <> struct toPlainIndex<_4> { static constexpr uint8_t Value = 3; };


} // namespace CCI


//
// Color channel selector
//
namespace internal {

    template <typename ColorT, uint8_t ChannelIndex, bool ChannelAvailable>
    struct SelectColorChannelImpl;

    template <typename ColorT, uint8_t ChannelIndex>
    struct SelectColorChannelImpl<ColorT, ChannelIndex, false> {
        static inline       uint16_t& _(      ColorT&) { return *reinterpret_cast<uint16_t*>(0); }
        static inline const uint16_t& _(const ColorT&) { return *reinterpret_cast<uint16_t*>(0); }
    };

    template <typename ColorT>
    struct SelectColorChannelImpl<ColorT, CCI::_1, true> {
        static inline       typename ColorT::Value0T& _(      ColorT& color) { return color.c0; }
        static inline const typename ColorT::Value0T& _(const ColorT& color) { return color.c0; }
    };

    template <typename ColorT>
    struct SelectColorChannelImpl<ColorT, CCI::_2, true> {
        static inline       typename ColorT::Value1T& _(      ColorT& color) { return color.c1; }
        static inline const typename ColorT::Value1T& _(const ColorT& color) { return color.c1; }
    };

    template <typename ColorT>
    struct SelectColorChannelImpl<ColorT, CCI::_3, true> {
        static inline       typename ColorT::Value2T& _(      ColorT& color) { return color.c2; }
        static inline const typename ColorT::Value2T& _(const ColorT& color) { return color.c2; }
    };

    template <typename ColorT>
    struct SelectColorChannelImpl<ColorT, CCI::_4, true> {
        static inline       typename ColorT::Value3T& _(      ColorT& color) { return color.c3; }
        static inline const typename ColorT::Value3T& _(const ColorT& color) { return color.c3; }
    };

} // namespace internal

template <typename ColorT, uint8_t ChannelIndex>
struct SelectColorChannel : internal::SelectColorChannelImpl< ColorT, ChannelIndex, !!(ChannelIndex & ColorT::ChannelAvailable) > {};


//
// Color channel copier
//
namespace internal {

    template <typename ColorT, uint8_t ChannelIndex, bool ChannelAvailable>
    struct CopyColorChannelImpl;

    template <typename ColorT, uint8_t ChannelIndex>
    struct CopyColorChannelImpl<ColorT, ChannelIndex, false> {
        static inline void _(ColorT&, const ColorT&) {}
    };

    template <typename ColorT>
    struct CopyColorChannelImpl<ColorT, CCI::_1, true> {
        static inline void _(ColorT& dst, const ColorT& src) { dst.c0 = src.c0; }
    };

    template <typename ColorT>
    struct CopyColorChannelImpl<ColorT, CCI::_2, true> {
        static inline void _(ColorT& dst, const ColorT& src) { dst.c1 = src.c1; }
    };

    template <typename ColorT>
    struct CopyColorChannelImpl<ColorT, CCI::_3, true> {
        static inline void _(ColorT& dst, const ColorT& src) { dst.c2 = src.c2; }
    };

    template <typename ColorT>
    struct CopyColorChannelImpl<ColorT, CCI::_4, true> {
        static inline void _(ColorT& dst, const ColorT& src) { dst.c3 = src.c3; }
    };

} // namespace internal

template <typename ColorT, uint8_t ChannelIndex>
struct CopyColorChannel : internal::CopyColorChannelImpl< ColorT, ChannelIndex, !!(ChannelIndex & ColorT::ChannelAvailable) > {};


//
// Color channel copier - with channel exclusion
//
template <typename ColorT, uint8_t ChannelIndex, uint8_t ExcludeIndex>
struct CopyColorChannelWx;

template <typename ColorT, uint8_t ExcludeIndex>
struct CopyColorChannelWx<ColorT, CCI::_1, ExcludeIndex> {
    static inline void _(ColorT& dst, const ColorT& src) { CopyColorChannel<ColorT, CCI::_1>::_(dst, src); }
};

template <typename ColorT>
struct CopyColorChannelWx<ColorT, CCI::_1, CCI::_1> {
    static inline void _(ColorT&, const ColorT&) {}
};

template <typename ColorT, uint8_t ExcludeIndex>
struct CopyColorChannelWx<ColorT, CCI::_2, ExcludeIndex> {
    static inline void _(ColorT& dst, const ColorT& src) { CopyColorChannel<ColorT, CCI::_2>::_(dst, src); }
};

template <typename ColorT>
struct CopyColorChannelWx<ColorT, CCI::_2, CCI::_2> {
    static inline void _(ColorT&, const ColorT&) {}
};

template <typename ColorT, uint8_t ExcludeIndex>
struct CopyColorChannelWx<ColorT, CCI::_3, ExcludeIndex> {
    static inline void _(ColorT& dst, const ColorT& src) { CopyColorChannel<ColorT, CCI::_3>::_(dst, src); }
};

template <typename ColorT>
struct CopyColorChannelWx<ColorT, CCI::_3, CCI::_3> {
    static inline void _(ColorT&, const ColorT&) {}
};

template <typename ColorT, uint8_t ExcludeIndex>
struct CopyColorChannelWx<ColorT, CCI::_4, ExcludeIndex> {
    static inline void _(ColorT& dst, const ColorT& src) { CopyColorChannel<ColorT, CCI::_4>::_(dst, src); }
};

template <typename ColorT>
struct CopyColorChannelWx<ColorT, CCI::_4, CCI::_4> {
    static inline void _(ColorT&, const ColorT&) {}
};


} // namespace eCxx


#endif
