/*
 * 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_LAYOUT_POLICY_BLOCK_ROW_MAJOR_INTER_ROW_MAJOR_H
#define ECXX_CTL_GFX_COLOR_BUFFER_LAYOUT_POLICY_BLOCK_ROW_MAJOR_INTER_ROW_MAJOR_H


namespace eCxx {


//
// Color buffer layout policy constants - block grouped & Y pixels interleaved
//
namespace CBP {

    struct BlockRowMajorPixelInterRowMajor;
    struct BlockRowMajorPixelInterRowMajorRXY;

    ////////////////////////////////////////////////////////////////////////////////////////////////////

    struct BlockRowMajorPixelInterAltRowMajor;
    struct BlockRowMajorPixelInterAltRowMajorRXY;

} // namespace CBP


//
// Implementation of color buffer block layout policy structures - block grouped & Y pixels interleaved
//
template <CBP::SUInt XSizeN, CBP::SUInt YSizeN, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN>
struct ColorBufferLayoutPolicy<XSizeN, YSizeN, BlockXSizeN, BlockYSizeN, InterYSizeN, CBP::BlockRowMajorPixelInterRowMajor> : public Uninstantiable {
    static constexpr CBP::DUInt BBlockXCountN = XSizeN / BlockXSizeN;
    static constexpr CBP::DUInt BBlockYCountN = YSizeN / BlockYSizeN;

    static constexpr CBP::DUInt IBlockYCountN = BlockYSizeN / InterYSizeN;
    static constexpr CBP::DUInt IRealXSizeN   = BlockXSizeN * InterYSizeN;

    /*
     * Visual and hardware pixel flow/movement example for 8x8 with block size 4x4 interleaved size 2 (memory address - grouped)
     *
     * Visual                           Hardware
     * |  0  1  2  3    4  5  6  7 |    |  0  1  2  3   16 17 18 19 |
     * |  8  9 10 11   12 13 14 15 |    |  8  9 10 11   24 25 26 27 |
     * | 16 17 18 19   20 21 22 23 |    |  4  5  6  7   20 21 22 23 |
     * | 24 25 26 27   28 29 30 31 |    | 12 13 14 15   28 29 30 31 |
     * |                           | -> |                           |
     * | 32 33 34 35   36 37 38 39 |    | 32 33 34 35   48 49 50 51 |
     * | 40 41 42 43   44 45 46 47 |    | 40 41 42 43   56 57 58 59 |
     * | 48 49 50 51   52 53 54 55 |    | 36 37 38 39   52 53 54 55 |
     * | 56 57 58 59   60 61 62 63 |    | 44 45 46 47   60 61 62 63 |
     *
     * Hardware pixel flow/movement example for 8x8 with block size 4x2 interleaved size 2 (pixel and memory address dump)
     *
     * | ( X, Y) ( X, Y) ( X, Y) ( X, Y) ( X, Y) ( X, Y) ( X, Y) ( X, Y) |   | Mem.Address             |
     * | ( 0, 0) ( 1, 0) ( 2, 0) ( 3, 0) ( 0, 2) ( 1, 2) ( 2, 2) ( 3, 2) |   |  0  1  2  3 16 17 18 19 |
     * | ( 0, 1) ( 1, 1) ( 2, 1) ( 3, 1) ( 0, 3) ( 1, 3) ( 2, 3) ( 3, 3) |   |  8  9 10 11 24 25 26 27 |
     * | ( 4, 0) ( 5, 0) ( 6, 0) ( 7, 0) ( 4, 2) ( 5, 2) ( 6, 2) ( 7, 2) |   |  4  5  6  7 20 21 22 23 |
     * | ( 4, 1) ( 5, 1) ( 6, 1) ( 7, 1) ( 4, 3) ( 5, 3) ( 6, 3) ( 7, 3) |   | 12 13 14 15 28 29 30 31 |
     * | ( 0, 4) ( 1, 4) ( 2, 4) ( 3, 4) ( 0, 6) ( 1, 6) ( 2, 6) ( 3, 6) |   | 32 33 34 35 48 49 50 51 |
     * | ( 0, 5) ( 1, 5) ( 2, 5) ( 3, 5) ( 0, 7) ( 1, 7) ( 2, 7) ( 3, 7) |   | 40 41 42 43 56 57 58 59 |
     * | ( 4, 4) ( 5, 4) ( 6, 4) ( 7, 4) ( 4, 6) ( 5, 6) ( 6, 6) ( 7, 6) |   | 36 37 38 39 52 53 54 55 |
     * | ( 4, 5) ( 5, 5) ( 6, 5) ( 7, 5) ( 4, 7) ( 5, 7) ( 6, 7) ( 7, 7) |   | 44 45 46 47 60 61 62 63 |
     */
    static inline CBP::DUInt _(CBP::DUInt x, CBP::DUInt y)
    {
        const CBP::DUInt bx = x / BlockXSizeN;
        const CBP::DUInt by = y / BlockYSizeN;

        const CBP::DUInt bm = (bx + by * BBlockXCountN) * BlockXSizeN * BlockYSizeN;

        const CBP::DUInt px = x % BlockXSizeN;
        const CBP::DUInt py = y % BlockYSizeN;

        const CBP::DUInt yd = py % InterYSizeN;
        const CBP::DUInt yg = py / InterYSizeN;

        const CBP::DUInt pm = yd * IRealXSizeN + yg * BlockXSizeN + px;

        return bm + pm;
    }
};


template <CBP::SUInt XSizeN, CBP::SUInt YSizeN, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN>
struct ColorBufferLayoutPolicy<XSizeN, YSizeN, BlockXSizeN, BlockYSizeN, InterYSizeN, CBP::BlockRowMajorPixelInterRowMajorRXY> : public Uninstantiable {
    static constexpr CBP::DUInt BBlockXCountN = XSizeN / BlockXSizeN;
    static constexpr CBP::DUInt BBlockYCountN = YSizeN / BlockYSizeN;

    static constexpr CBP::DUInt IBlockYCountN = BlockYSizeN / InterYSizeN;
    static constexpr CBP::DUInt IRealXSizeN   = BlockXSizeN * InterYSizeN;

    /*
     * Visual and hardware pixel flow/movement example for 8x8 with block size 4x4 interleaved size 2 (memory address - grouped)
     *
     * Visual                           Hardware
     * |  0  1  2  3    4  5  6  7 |    | 63 62 61 60   47 46 45 44 |
     * |  8  9 10 11   12 13 14 15 |    | 55 54 53 52   39 38 37 36 |
     * | 16 17 18 19   20 21 22 23 |    | 59 58 57 56   43 42 41 40 |
     * | 24 25 26 27   28 29 30 31 |    | 51 50 49 48   35 34 33 32 |
     * |                           | -> |                           |
     * | 32 33 34 35   36 37 38 39 |    | 31 30 29 28   15 14 13 12 |
     * | 40 41 42 43   44 45 46 47 |    | 23 22 21 20    7  6  5  4 |
     * | 48 49 50 51   52 53 54 55 |    | 27 26 25 24   11 10  9  8 |
     * | 56 57 58 59   60 61 62 63 |    | 19 18 17 16    3  2  1  0 |
     *
     * Hardware pixel flow/movement example for 8x8 with block size 4x2 interleaved size 2 (pixel and memory address dump)
     *
     * | ( X, Y) ( X, Y) ( X, Y) ( X, Y) ( X, Y) ( X, Y) ( X, Y) ( X, Y) |   | Mem.Address             |
     * | ( 7, 7) ( 6, 7) ( 5, 7) ( 4, 7) ( 7, 5) ( 6, 5) ( 5, 5) ( 4, 5) |   | 63 62 61 60 47 46 45 44 |
     * | ( 7, 6) ( 6, 6) ( 5, 6) ( 4, 6) ( 7, 4) ( 6, 4) ( 5, 4) ( 4, 4) |   | 55 54 53 52 39 38 37 36 |
     * | ( 3, 7) ( 2, 7) ( 1, 7) ( 0, 7) ( 3, 5) ( 2, 5) ( 1, 5) ( 0, 5) |   | 59 58 57 56 43 42 41 40 |
     * | ( 3, 6) ( 2, 6) ( 1, 6) ( 0, 6) ( 3, 4) ( 2, 4) ( 1, 4) ( 0, 4) |   | 51 50 49 48 35 34 33 32 |
     * | ( 7, 3) ( 6, 3) ( 5, 3) ( 4, 3) ( 7, 1) ( 6, 1) ( 5, 1) ( 4, 1) |   | 31 30 29 28 15 14 13 12 |
     * | ( 7, 2) ( 6, 2) ( 5, 2) ( 4, 2) ( 7, 0) ( 6, 0) ( 5, 0) ( 4, 0) |   | 23 22 21 20  7  6  5  4 |
     * | ( 3, 3) ( 2, 3) ( 1, 3) ( 0, 3) ( 3, 1) ( 2, 1) ( 1, 1) ( 0, 1) |   | 27 26 25 24 11 10  9  8 |
     * | ( 3, 2) ( 2, 2) ( 1, 2) ( 0, 2) ( 3, 0) ( 2, 0) ( 1, 0) ( 0, 0) |   | 19 18 17 16  3  2  1  0 |
     */
    static inline CBP::DUInt _(CBP::DUInt x, CBP::DUInt y)
    {
        x = XSizeN - 1 - x;
        y = YSizeN - 1 - y;

        const CBP::DUInt bx = x / BlockXSizeN;
        const CBP::DUInt by = y / BlockYSizeN;

        const CBP::DUInt bm = (bx + by * BBlockXCountN) * BlockXSizeN * BlockYSizeN;

        const CBP::DUInt px = x % BlockXSizeN;
        const CBP::DUInt py = y % BlockYSizeN;

        const CBP::DUInt yd = py % InterYSizeN;
        const CBP::DUInt yg = py / InterYSizeN;

        const CBP::DUInt pm = yd * IRealXSizeN + yg * BlockXSizeN + px;

        return bm + pm;
    }
};


////////////////////////////////////////////////////////////////////////////////////////////////////


template <CBP::SUInt XSizeN, CBP::SUInt YSizeN, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN>
struct ColorBufferLayoutPolicy<XSizeN, YSizeN, BlockXSizeN, BlockYSizeN, InterYSizeN, CBP::BlockRowMajorPixelInterAltRowMajor> : public Uninstantiable {
    static constexpr CBP::DUInt BBlockXCountN = XSizeN / BlockXSizeN;
    static constexpr CBP::DUInt BBlockYCountN = YSizeN / BlockYSizeN;

    static constexpr CBP::DUInt IBlockYCountN = BlockYSizeN / InterYSizeN;
    static constexpr CBP::DUInt IRealXSizeN   = BlockXSizeN * InterYSizeN;

    /*
     * Visual and hardware pixel flow/movement example for 8x8 with block size 4x4 interleaved size 2 (memory address - grouped)
     *
     * Visual                           Hardware
     * |  0  1  2  3    4  5  6  7 |    |  0  1  2  3   16 17 18 19 |
     * |  8  9 10 11   12 13 14 15 |    |  8  9 10 11   24 25 26 27 |
     * | 16 17 18 19   20 21 22 23 |    |  7  6  5  4   23 22 21 20 |
     * | 24 25 26 27   28 29 30 31 |    | 15 14 13 12   31 30 29 28 |
     * |                           | -> |                           |
     * | 32 33 34 35   36 37 38 39 |    | 32 33 34 35   48 49 50 51 |
     * | 40 41 42 43   44 45 46 47 |    | 40 41 42 43   56 57 58 59 |
     * | 48 49 50 51   52 53 54 55 |    | 39 38 37 36   55 54 53 52 |
     * | 56 57 58 59   60 61 62 63 |    | 47 46 45 44   63 62 61 60 |
     *
     * Hardware pixel flow/movement example for 8x8 with block size 4x2 interleaved size 2 (pixel and memory address dump)
     *
     * | ( X, Y) ( X, Y) ( X, Y) ( X, Y) ( X, Y) ( X, Y) ( X, Y) ( X, Y) |   | Mem.Address             |
     * | ( 0, 0) ( 1, 0) ( 2, 0) ( 3, 0) ( 3, 2) ( 2, 2) ( 1, 2) ( 0, 2) |   |  0  1  2  3 19 18 17 16 |
     * | ( 0, 1) ( 1, 1) ( 2, 1) ( 3, 1) ( 3, 3) ( 2, 3) ( 1, 3) ( 0, 3) |   |  8  9 10 11 27 26 25 24 |
     * | ( 4, 0) ( 5, 0) ( 6, 0) ( 7, 0) ( 7, 2) ( 6, 2) ( 5, 2) ( 4, 2) |   |  4  5  6  7 23 22 21 20 |
     * | ( 4, 1) ( 5, 1) ( 6, 1) ( 7, 1) ( 7, 3) ( 6, 3) ( 5, 3) ( 4, 3) |   | 12 13 14 15 31 30 29 28 |
     * | ( 0, 4) ( 1, 4) ( 2, 4) ( 3, 4) ( 3, 6) ( 2, 6) ( 1, 6) ( 0, 6) |   | 32 33 34 35 51 50 49 48 |
     * | ( 0, 5) ( 1, 5) ( 2, 5) ( 3, 5) ( 3, 7) ( 2, 7) ( 1, 7) ( 0, 7) |   | 40 41 42 43 59 58 57 56 |
     * | ( 4, 4) ( 5, 4) ( 6, 4) ( 7, 4) ( 7, 6) ( 6, 6) ( 5, 6) ( 4, 6) |   | 36 37 38 39 55 54 53 52 |
     * | ( 4, 5) ( 5, 5) ( 6, 5) ( 7, 5) ( 7, 7) ( 6, 7) ( 5, 7) ( 4, 7) |   | 44 45 46 47 63 62 61 60 |
     */
    static inline CBP::DUInt _(CBP::DUInt x, CBP::DUInt y)
    {
        const CBP::DUInt bx = x / BlockXSizeN;
        const CBP::DUInt by = y / BlockYSizeN;

        const CBP::DUInt bm = (bx + by * BBlockXCountN) * BlockXSizeN * BlockYSizeN;

        const CBP::DUInt px = x % BlockXSizeN;
        const CBP::DUInt py = y % BlockYSizeN;

        const CBP::DUInt yd = py % InterYSizeN;
        const CBP::DUInt yg = py / InterYSizeN;

        const CBP::DUInt pm = (yg & 1) ? ( yd * IRealXSizeN + yg * BlockXSizeN + (BlockXSizeN - 1 - px) )
                                       : ( yd * IRealXSizeN + yg * BlockXSizeN +                    px  );

        return bm + pm;
    }
};


template <CBP::SUInt XSizeN, CBP::SUInt YSizeN, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN>
struct ColorBufferLayoutPolicy<XSizeN, YSizeN, BlockXSizeN, BlockYSizeN, InterYSizeN, CBP::BlockRowMajorPixelInterAltRowMajorRXY> : public Uninstantiable {
    static constexpr CBP::DUInt BBlockXCountN = XSizeN / BlockXSizeN;
    static constexpr CBP::DUInt BBlockYCountN = YSizeN / BlockYSizeN;

    static constexpr CBP::DUInt IBlockYCountN = BlockYSizeN / InterYSizeN;
    static constexpr CBP::DUInt IRealXSizeN   = BlockXSizeN * InterYSizeN;

    /*
     * Visual and hardware pixel flow/movement example for 8x8 with block size 4x4 interleaved size 2 (memory address - grouped)
     *
     * Visual                           Hardware
     * |  0  1  2  3    4  5  6  7 |    | 60 61 62 63   44 45 46 47 |
     * |  8  9 10 11   12 13 14 15 |    | 52 53 54 55   36 37 38 39 |
     * | 16 17 18 19   20 21 22 23 |    | 59 58 57 56   43 42 41 40 |
     * | 24 25 26 27   28 29 30 31 |    | 51 50 49 48   35 34 33 32 |
     * |                           | -> |                           |
     * | 32 33 34 35   36 37 38 39 |    | 28 29 30 31   12 13 14 15 |
     * | 40 41 42 43   44 45 46 47 |    | 20 21 22 23    4  5  6  7 |
     * | 48 49 50 51   52 53 54 55 |    | 27 26 25 24   11 10  9  8 |
     * | 56 57 58 59   60 61 62 63 |    | 19 18 17 16    3  2  1  0 |
     *
     * Hardware pixel flow/movement example for 8x8 with block size 4x2 interleaved size 2 (pixel and memory address dump)
     *
     * | ( X, Y) ( X, Y) ( X, Y) ( X, Y) ( X, Y) ( X, Y) ( X, Y) ( X, Y) |   | Mem.Address             |
     * | ( 7, 7) ( 6, 7) ( 5, 7) ( 4, 7) ( 4, 5) ( 5, 5) ( 6, 5) ( 7, 5) |   | 63 62 61 60 44 45 46 47 |
     * | ( 7, 6) ( 6, 6) ( 5, 6) ( 4, 6) ( 4, 4) ( 5, 4) ( 6, 4) ( 7, 4) |   | 55 54 53 52 36 37 38 39 |
     * | ( 3, 7) ( 2, 7) ( 1, 7) ( 0, 7) ( 0, 5) ( 1, 5) ( 2, 5) ( 3, 5) |   | 59 58 57 56 40 41 42 43 |
     * | ( 3, 6) ( 2, 6) ( 1, 6) ( 0, 6) ( 0, 4) ( 1, 4) ( 2, 4) ( 3, 4) |   | 51 50 49 48 32 33 34 35 |
     * | ( 7, 3) ( 6, 3) ( 5, 3) ( 4, 3) ( 4, 1) ( 5, 1) ( 6, 1) ( 7, 1) |   | 31 30 29 28 12 13 14 15 |
     * | ( 7, 2) ( 6, 2) ( 5, 2) ( 4, 2) ( 4, 0) ( 5, 0) ( 6, 0) ( 7, 0) |   | 23 22 21 20  4  5  6  7 |
     * | ( 3, 3) ( 2, 3) ( 1, 3) ( 0, 3) ( 0, 1) ( 1, 1) ( 2, 1) ( 3, 1) |   | 27 26 25 24  8  9 10 11 |
     * | ( 3, 2) ( 2, 2) ( 1, 2) ( 0, 2) ( 0, 0) ( 1, 0) ( 2, 0) ( 3, 0) |   | 19 18 17 16  0  1  2  3 |
     */
    static inline CBP::DUInt _(CBP::DUInt x, CBP::DUInt y)
    {
        x = XSizeN - 1 - x;
        y = YSizeN - 1 - y;

        const CBP::DUInt bx = x / BlockXSizeN;
        const CBP::DUInt by = y / BlockYSizeN;

        const CBP::DUInt bm = (bx + by * BBlockXCountN) * BlockXSizeN * BlockYSizeN;

        const CBP::DUInt px = x % BlockXSizeN;
        const CBP::DUInt py = y % BlockYSizeN;

        const CBP::DUInt yd = py % InterYSizeN;
        const CBP::DUInt yg = py / InterYSizeN;

        const CBP::DUInt pm = (yg & 1) ? ( yd * IRealXSizeN + yg * BlockXSizeN + (BlockXSizeN - 1 - px) )
                                       : ( yd * IRealXSizeN + yg * BlockXSizeN +                    px  );

        return bm + pm;
    }
};


} // namespace eCxx


#endif
