#include "../../internal-include/GPU/gpu_internal.hpp"
#include <PSX/System/IOPorts/interrupt_io.hpp>
#include <PSX/Auxiliary/lz4_decompressor.hpp>
#include <PSX/File/Processor/file_processor.hpp>
#include <PSX/GPU/gpu.hpp>
#include <PSX/System/syscalls.hpp>
#include <stdio.hpp>

#ifdef JABYENGINE_PAL
    #include "splash_image_pal_boot.hpp"
#else
    #include "splash_image_ntsc_boot.hpp"
#endif //JABYENGINE_PAL

// Concept for switchting BIOS Fonts...?
#include "BIOSFont/ascii_bios_font.hpp"

extern "C" uint32_t __boot_loader_end;

namespace JabyEngine {
    namespace GPU {
        namespace internal {
            extern SysCall::InterruptCallback irq_callback;
        }

        namespace SJIS {
            void load_clut(const PositionU16& dst_cord) {
                struct CLUT {
                    CPU2VRAM cmd;
                    Color    data[16];
                };
                const CLUT clut {
                    .cmd  = CPU2VRAM::create(AreaU16::create(dst_cord, GPU::SizeU16::create(16, 1))),
                    .data = {
                        Color::Black(), Color::Grey(), 
                        Color::Black(), Color::Black(), Color::Black(), Color::Black(), Color::Black(), Color::Black(), Color::Black(),
                        Color::Black(), Color::Black(), Color::Black(), Color::Black(), Color::Black(), Color::Black(), Color::Black(), 
                    }
                };

                GPU::internal::render(reinterpret_cast<const uint32_t*>(&clut), sizeof(CLUT)/sizeof(uint32_t));
            }
        }
    }

    namespace boot {
        namespace GPU {
            using namespace JabyEngine::GPU;

            static void configure_display() {
                GPU_IO::GP1.set_display_mode(::JabyEngine::GPU::internal::Display::DisplayMode);
                GPU::Display::set_offset(0, 0);
            }
            
            static size_t decompress_logo() {
                LZ4Decompressor lz4_decomp(reinterpret_cast<uint8_t*>(&__boot_loader_end));

                const auto [progress, bytes_ready] = lz4_decomp.process(ArrayRange(SplashScreen, sizeof(SplashScreen)), true);
                switch(progress) {
                    case Progress::InProgress:
                        printf("Decompressing still in progress... %llu\n", bytes_ready);
                        break;

                    case Progress::Error:
                        printf("Error decompressing!!!\n");
                        break;

                    case Progress::Done:
                        printf("Done decompressing: %llu Bytes ready\n", bytes_ready);
                        break;
                }

                return bytes_ready;
            }

            void display_logo() {
                static constexpr uint16_t TexturePageHeight = 256;
                const auto bytes_ready = decompress_logo();

                // Upload SplashScreen picture
                auto state = FileProcessor::create(&__boot_loader_end, SimpleTIM::create(32, 0, 0, 0));
                state.process(bytes_ready);

                // Now load the BIOS font to the specified location
                SJIS::load(BIOS_Font::TextureLoadPos);
                SJIS::load_clut(BIOS_Font::CLUTLoadPos);

                // Duplicate DisplayBuffer content
                ::JabyEngine::GPU::internal::copy_vram_to_vram({PositionU16::create(0, TexturePageHeight), SizeU16::create(Display::Width, TexturePageHeight)}, PositionU16::create(0, 0));

                Display::enable();
            }

            void setup() {
                GPU_IO::GP1.reset();
                configure_display();
                ::JabyEngine::GPU::internal::Display::exchange_buffer_and_display();
                
                GPU::internal::wait_ready_for_CMD();
                GPU::internal::quick_fill_fast(Color24::Black(), {PositionU16::create(0, 0), SizeU16::create(Display::Width, Display::Height)});

                SysCall::EnterCriticalSection();
                    SysCall::SysEnqIntRP(SysCall::Priority::VblankIrq, &::JabyEngine::GPU::internal::irq_callback);
                    Interrupt::enable_irq(Interrupt::VBlank);                
                SysCall::ExitCriticalSection();
            }
        }
    }
}