#pragma once
#include "linked_elements.hpp"
#include "primitive_support_types.hpp"

namespace JabyEngine {
    namespace GPU {
        struct OffsetPageWithClut {
            PageOffset tex_offset;
            PageClut   clut;

            static constexpr OffsetPageWithClut create(PageOffset tex_offset, PageClut clut) {
                return OffsetPageWithClut{tex_offset, clut};
            }
        };

        namespace internal {
            struct RectCode : public CodeBase<RectCode> {
                enum struct SizeType : uint8_t {
                    Variable    = 0,
                    Pixel1x1    = 1,
                    Sprite8x8   = 2,
                    Sprite16x16 = 3
                };
                static constexpr uint8_t CmdValue = 0b011;

                static constexpr auto Size = BitRange::from_to(27 - BitCorrection, 28 - BitCorrection);

                static constexpr RectCode create() {
                    return CodeBase<RectCode>::create(CmdValue);
                }
            };

            template<typename T>
            struct RectCodeInterface {
                typedef typename ::JabyEngine::GPU::internal::RectCode Code;

                constexpr T& set_semi_transparent(bool set = true) {
                    static_cast<T*>(this)->code.set_tenary(set, Code::SemiTransparent, Code::NonTransparent);
                    return *static_cast<T*>(this);
                }
            };

            template<typename RectCode::SizeType Size> 
            struct RECT_BASE_F : public RectCodeInterface<RECT_BASE_F<Size>> {
                typedef RectCodeInterface<RECT_BASE_F<Size>>::Code Code;
                static constexpr auto IdentityCode = Code::create().set(Code::Size.with(static_cast<uint8_t>(Size))).set(Code::Untextured).set(Code::NonTransparent);

                Color24 color;
                Code    code;
                Vertex  position;

                static constexpr RECT_BASE_F create(const Vertex& position, const Color24& color) {
                    return RECT_BASE_F {
                        .color = color, .code = IdentityCode, .position = position 
                    };
                }
            };

            template<typename RectCode::SizeType Size> 
            struct RECT_BASE_T : public RectCodeInterface<RECT_BASE_T<Size>> {
                typedef RectCodeInterface<RECT_BASE_T<Size>>::Code Code;
                static constexpr auto IdentityCode = Code(RECT_BASE_F<Size>::IdentityCode).set(Code::Textured);

                Color24    color;
                Code       code;
                Vertex     position;
                PageOffset tex_offset;
                PageClut   clut;

                static constexpr RECT_BASE_T create(const Vertex& position, const OffsetPageWithClut& tex_offset_w_clut, const Color24& color = Color24::Grey()) {
                    return RECT_BASE_T {
                        .color = color, .code = IdentityCode, .position = position, .tex_offset = tex_offset_w_clut.tex_offset, .clut = tex_offset_w_clut.clut
                    };
                }
            };

            template<typename RectCode::SizeType Size> 
            struct RECT_F : public RECT_BASE_F<Size>, public internal::RenderPrimitive<RECT_F<Size>>, public internal::LinkedElementCreator<RECT_F<Size>> {
                static constexpr RECT_F create(const Vertex& position, const Color24& color) {
                    RECT_F rect;

                    static_cast<RECT_BASE_F<Size>&>(rect) = RECT_BASE_F<Size>::create(position, color);
                    return rect;
                }      
            };

            template<typename RectCode::SizeType Size> 
            struct RECT_T : public RECT_BASE_T<Size>, public internal::RenderPrimitive<RECT_T<Size>>, public internal::LinkedElementCreator<RECT_T<Size>> {
                static constexpr RECT_T create(const Vertex& position, const OffsetPageWithClut& tex_offset_w_clut, const Color24& color = Color24::Grey()) {                    
                    RECT_T rect;
                    
                    static_cast<RECT_BASE_T<Size>&>(rect) = RECT_BASE_T<Size>::create(position, tex_offset_w_clut, color);
                    return rect;
                }
            };
        }

        typedef internal::RECT_F<internal::RectCode::SizeType::Pixel1x1>    TILE_1;
        typedef internal::RECT_F<internal::RectCode::SizeType::Sprite8x8>   TILE_8;
        typedef internal::RECT_F<internal::RectCode::SizeType::Sprite16x16> TILE_16;
        struct TILE : public internal::RECT_BASE_F<internal::RectCode::SizeType::Variable>, public internal::RenderPrimitive<TILE>, public internal::LinkedElementCreator<TILE> {
            SizeI16 size;

            static constexpr TILE create(const AreaI16& area, const Color24& color) {
                TILE tile;

                static_cast<RECT_BASE_F&>(tile) = RECT_BASE_F::create(area.position, color);
                tile.size                       = area.size;
                return tile;
            }
        };

        typedef internal::RECT_T<internal::RectCode::SizeType::Pixel1x1>    SPRT_1;
        typedef internal::RECT_T<internal::RectCode::SizeType::Sprite8x8>   SPRT_8;
        typedef internal::RECT_T<internal::RectCode::SizeType::Sprite16x16> SPRT_16;
        struct SPRT : public internal::RECT_BASE_T<internal::RectCode::SizeType::Variable>, public internal::RenderPrimitive<SPRT>, public internal::LinkedElementCreator<SPRT> {
            SizeI16 size;

            static constexpr SPRT create(const AreaI16& area, const OffsetPageWithClut& page, const Color24& color = Color24::Grey()) {
                SPRT sprt;

                static_cast<RECT_BASE_T&>(sprt) = RECT_BASE_T::create(area.position, page, color);
                sprt.size                       = area.size;
                return sprt;
            }
        };
    }
}