#pragma once
#include "IOValues/gpu_io_values.hpp"

namespace JabyEngine {
    namespace GPU_IO {
        struct GP0_IO : public IOPort<GPU_IO_Values::GP0> {
            void clear_cache() {
                this->write(GPU_IO_Values::GP0::ClearCache());
            }

            void quick_fill(GPU::Color24 color) {
                this->write(GPU_IO_Values::GP0::QuickFill(color));
            }

            void set_vram2vram_blitting() {
                this->write(GPU_IO_Values::GP0::VRAM2VRAMBlitting());
            }

            void set_cpu2vram_blitting() {
                this->write(GPU_IO_Values::GP0::CPU2VRAMBlitting());
            }

            void set_tex_page(const GPU::PositionU16& page_pos, GPU::SemiTransparency transparency, GPU::TextureColorMode tex_color, bool dither, bool draw_on_display_area) {
                this->write(GPU_IO_Values::GP0::TexPage(page_pos, transparency, tex_color, dither, draw_on_display_area));
            }

            void set_draw_area_top_left(const GPU::PositionU16& position) {
                this->write(GPU_IO_Values::GP0::DrawAreaTopLeft(position));
            }

            void set_draw_area_bottom_right(const GPU::PositionU16& position) {
                this->write(GPU_IO_Values::GP0::DrawAreaBottomRight(position));
            }

            void set_draw_offset(const GPU::PositionI16& offset) {
                this->write(GPU_IO_Values::GP0::DrawOffset(offset));
            }

            void pass_top_left_position(const GPU::PositionU16& position) {
                this->write(GPU_IO_Values::GP0::PostionTopLeft(position));
            }

            void pass_width_height(const GPU::SizeU16& size) {
                this->write(GPU_IO_Values::GP0::WidthHeight(size));
            }
        };
        
        struct GP1_IO : public IOPort<GPU_IO_Values::GP1> {
            void reset() {
                this->write(GPU_IO_Values::GP1::Reset());
            }

            void reset_cmd_buffer() {
                this->write(GPU_IO_Values::GP1::ResetCMDBuffer());
            }

            void set_display_state(GPU_IO_Values::DisplayMode::State state) {
                this->write(GPU_IO_Values::GP1::DisplayState(state));
            }

            void set_dma_direction(GPU_IO_Values::GPUSTAT::DMADirection dir) {
                this->write(GPU_IO_Values::GP1::DMADirection(dir));
            }

            void set_display_area(const GPU::PositionU16& position) {
                this->write(GPU_IO_Values::GP1::DisplayArea(position));
            }

            void set_horizontal_display_range(uint16_t x1, uint16_t x2) {
                this->write(GPU_IO_Values::GP1::HorizontalDisplayRange(x1, x2));
            }

            void set_vertical_display_range(uint16_t y1, uint16_t y2) {
                this->write(GPU_IO_Values::GP1::VerticalDisplayRange(y1, y2));
            }

            void set_display_mode(GPU_IO_Values::DisplayMode mode) {
                this->write(GPU_IO_Values::GP1::DisplayMode(mode));
            }
        };
        using GPUREAD_IO = IOPort<GPU_IO_Values::GPUREAD>;
        using GPUSTAT_IO = IOPort<GPU_IO_Values::GPUSTAT>;

        static constexpr size_t FIFOWordSize = 16;

        static auto&       GP0     = __declare_io_port(GP0_IO,      0x1F801810);
        static const auto& GPUREAD = __declare_io_port(GPUREAD_IO, 0x1F801810);
        static auto&       GP1     = __declare_io_port(GP1_IO,      0x1F801814);
        static const auto& GPUSTAT = __declare_io_port(GPUSTAT_IO, 0x1F801814);
    }
}