#pragma once
#include "../Auxiliary/bits.hpp"
#include "../GPU/gpu_types.hpp"
#include "../jabyengine_defines.hpp"
#include "../SPU/spu.hpp"

namespace JabyEngine {
    #pragma pack(push, 1)
    struct Nothing {
    };

    struct CopyTo {
        uint32_t* dst;
    };

    struct SimpleTIM {
        static constexpr auto TextureX = BitRange::from_to(0, 8);
        static constexpr auto TextureY = BitRange::from_to(9, 16);
        static constexpr auto ClutX    = BitRange::from_to(17, 22);
        static constexpr auto ClutY    = BitRange::from_to(23, 31);

        uint32_t raw;

        static constexpr SimpleTIM create(uint16_t texX, uint16_t texY, uint16_t clutX, uint16_t clutY) {
            return SimpleTIM{.raw = TextureX.as_value(texX >> 1) | TextureY.as_value(texY >> 1) | ClutX.as_value(clutX >> 4) | ClutY.as_value(static_cast<uint32_t>(clutY))};
        }

        constexpr uint16_t get_texture_x() const {
            return bit::value::get_normalized(this->raw, TextureX) << 1;
        }

        constexpr uint16_t get_texture_y() const {
            return bit::value::get_normalized(this->raw, TextureY) << 1;
        }

        constexpr GPU::PositionU16 get_texture_position() const {
            return GPU::PositionU16::create(SimpleTIM::get_texture_x(), SimpleTIM::get_texture_y());
        }

        constexpr uint16_t get_clut_x() const {
            return bit::value::get_normalized(this->raw, SimpleTIM::ClutX) << 4;
        }

        constexpr uint16_t get_clut_y() const {
            return bit::value::get_normalized(this->raw, ClutY);
        }

        constexpr GPU::PositionU16 get_clut_position() const {
            return GPU::PositionU16::create(SimpleTIM::get_clut_x(), SimpleTIM::get_clut_y());
        }

        constexpr GPU::PageOffset get_page_offset() const {
            const auto tex_page = SimpleTIM::get_texture_position();
            return GPU::PageOffset::create(tex_page.x&0x3F, tex_page.y);
        }

        constexpr GPU::PageOffset get_page_offset_clut8() const {
            const auto page_offset = SimpleTIM::get_page_offset();
            return GPU::PageOffset::create(page_offset.x*2, page_offset.y);
        }

        constexpr GPU::PageOffset get_page_offset_clut4() const {
            const auto page_offset = SimpleTIM::get_page_offset();
            return GPU::PageOffset::create(page_offset.x*4, page_offset.y);
        }
    };

    struct TIM {
        uint32_t zero;

        static constexpr TIM create() {
            return TIM{.zero = 0};
        }
    };

    struct VAG {
        uint8_t           voice_number;
        SPU::SimpleVolume inital_stereo_vol;

        static constexpr VAG create(uint8_t voice_num, SPU::SimpleVolume volume) {
            return VAG{.voice_number = voice_num, .inital_stereo_vol = volume};
        }
    };

    #pragma pack(pop)

    using Overlay = CopyTo;
}