#pragma once
#include "../../Auxiliary/type_traits.hpp"
#include "../../System/IOPorts/gpu_io.hpp"
#include "../gpu_types.hpp"

namespace JabyEngine {
    namespace GPU {
        namespace internal {
            template<typename T>
            struct CodeBase {
                static constexpr auto BitCorrection = 24;
                static constexpr auto CmdID         = BitRange::from_to(29 - BitCorrection, 31 - BitCorrection);

                uint8_t value;

                // Common values for all the primitves
                static constexpr auto Textured        = Bit(26 - BitCorrection);
                static constexpr auto Untextured      = !Textured;
                static constexpr auto SemiTransparent = Bit(25 - BitCorrection);
                static constexpr auto NonTransparent  = !SemiTransparent;
                static constexpr auto NoBlendTexture  = Bit(24 - BitCorrection);
                static constexpr auto BlendTexture    = !NoBlendTexture;

                static constexpr T create(uint8_t value) {
                    return T{bit::value::set_normalized<uint8_t>(0u, CmdID.with(value))};
                }

                static constexpr T create(const T& code) {
                    return CodeBase::create(code.value);
                }

                constexpr T& set(Bit bit) {
                    this->value = bit::set(this->value, bit);
                    return static_cast<T&>(*this);
                }

                constexpr T& set(ClearBit bit) {
                    this->value = bit::set(this->value, bit);
                    return static_cast<T&>(*this);
                }

                constexpr T& set(BitRange::RangeValuePair<uint8_t> value_pair) {
                    this->value = bit::value::set_normalized(this->value, value_pair);
                    return static_cast<T&>(*this);
                }

                template<typename S, typename R>
                constexpr T& set_tenary(bool use_first, const S& first, const R& second) {
                    if(use_first) {
                        return this->set(first);
                    }

                    else {
                        return this->set(second);
                    }
                }
            };

            template<typename T>
            struct RenderPrimitive {
                static constexpr bool is_render_primitive = true;

                void set_identitiy() {
                    static_cast<T&>(*this).code = T::IdentityCode;
                }
            };
        }

        struct PageClut {
            uint16_t value;
            
            static constexpr PageClut create(uint16_t x, uint16_t y) {
                return PageClut{static_cast<uint16_t>((y << 6) | ((x >> 4) & 0x3f))};
            }

            static constexpr PageClut create(const PositionU16& clut_pos) {
                return PageClut::create(clut_pos.x, clut_pos.y);
            }
        };

        struct TPage {
            static constexpr auto TexturePageX     = BitRange::from_to(0, 3);
            static constexpr auto TexturePageY     = BitRange::from_to(4, 4);
            static constexpr auto SemiTransparency = BitRange::from_to(5, 6);
            static constexpr auto TextureClut      = BitRange::from_to(7, 8);

            uint16_t value;

            static constexpr TPage create(uint16_t x, uint16_t y, ::JabyEngine::GPU::SemiTransparency transparency, TextureColorMode clut_color) {
                return TPage{static_cast<uint16_t>(TexturePageX.as_value(x >> 6) | TexturePageY.as_value(y >> 8) | SemiTransparency.as_value(static_cast<uint16_t>(transparency)) | TextureClut.as_value(static_cast<uint16_t>(clut_color)))};
            }

            static constexpr TPage create(const PositionU16& pos, ::JabyEngine::GPU::SemiTransparency transparency, TextureColorMode clut_color) {
                return TPage::create(pos.x, pos.y, transparency, clut_color);
            }
        };
    }
}