#include "../include/asset_mgr.hpp"
#include "Custom/custom_files.hpp"
#include "Overlay/Overlays.hpp"
#include <PSX/Audio/CDXA.hpp>
#include <PSX/AutoLBA/auto_lba.hpp>
#include <PSX/AutoLBA/auto_lba_declaration.hpp>
#include <PSX/File/Processor/cd_file_processor.hpp>
#include <stdio.hpp>

extern "C" uint32_t __bios_info_start;
extern "C" uint32_t __controller_tests_start;
extern "C" uint32_t __gpu_tests_start;
extern "C" uint32_t __gte_tests_start;
extern "C" uint32_t __font_cycler_start;
extern "C" uint32_t __screen_center_start;

namespace Assets {
    enum LBA {
        __jabyengine_start_lba_request
        __jabyengine_request_lba_for(PACO,            "ASSETS/MAIN/PACO.IMG"),
        __jabyengine_request_lba_for(DFISH,           "ASSETS/MAIN/DFISH.IMG"),
        __jabyengine_request_lba_for(JABY_LOAD,       "ASSETS/MAIN/LOAD.IMG"),
        __jabyengine_request_lba_for(APPLE_SFX,       "SFX/APPLE.VAG"),
        __jabyengine_request_lba_for(BLUBB_SFX,       "SFX/BLUBB.VAG"),
        __jabyengine_request_lba_for(FRIEND_SFX,      "SFX/FRIEND.VAG"),
        __jabyengine_request_lba_for(MIX_XA,          "XAAUDIO/MIX.XA"),
        __jabyengine_request_lba_for(BIOS_INFO_OVL,   "BIO.BIN"),
        __jabyengine_request_lba_for(GPU_TEST_OVL,    "GTO.BIN"),
        __jabyengine_request_lba_for(GTE_TEST_OVL,    "GTE.BIN"),
        __jabyengine_request_lba_for(CONT_TEST_OVL,   "CTO.BIN"),
        __jabyengine_request_lba_for(FONT_CYC_OVL,    "FCO.BIN"),
        __jabyengine_request_lba_for(SCREEN_CENT_OVL, "SCO.BIN"),
        __jabyengine_end_lba_request
    };
    __declare_lba_header(LBA);

    static void load(const volatile AutoLBAEntry* lba, const CDFile* assets, size_t size) {
        const auto      buffer_cfg = CDFileProcessor::BufferConfiguration::new_default();
        CDFileProcessor file_processor;

        file_processor.setup(lba, CDFileProcessor::JobArray{assets, size}, buffer_cfg);
        while(true) {
            switch(file_processor.process()) {
                case Progress::InProgress:
                    break;

                case Progress::Done:
                    if(!file_processor.next(lba, buffer_cfg)) {
                        return;
                    }
                    break;
                
                case Progress::Error:
                    printf("Error detected! Aborting load\n");
                    return;
            }
        }
        file_processor.shutdown();
    }

    template<size_t N>
    static void load(const volatile AutoLBAEntry* lba, const CDFile (&files)[N]) {
        return load(lba, files, N);
    }

    namespace Main {
        using SPU::operator""_vol;

        static const CDFile Files[] = {
            CDFileBuilder::simple_tim(LBA::PACO,      PacoTIM),
            CDFileBuilder::simple_tim(LBA::DFISH,     DoenerFishInfo.tim),
            CDFileBuilder::simple_tim(LBA::JABY_LOAD, JabyLoader::TIMLoaction),
            CDFileBuilder::sony_vag(LBA::APPLE_SFX,   VAG::create(0, 1.0_vol)),
            CDFileBuilder::sony_vag(LBA::BLUBB_SFX,   VAG::create(1, 1.0_vol)),
            CDFileBuilder::sony_vag(LBA::FRIEND_SFX,  VAG::create(2, 1.0_vol)),
            CustomCDFileBuilder::jingle(2),
        };

        void load() {
            ::Assets::load(lba, Files);
        }
    }

    namespace Overlay {
        static void load(const CDFile& overlay_file, const volatile AutoLBAEntry* overlay_lba, const CDFile* overlay_assets, size_t size) {
            ::Assets::load(lba, &overlay_file, 1);
            if(overlay_lba) {
                ::Assets::load(overlay_lba, overlay_assets, size);
            }
        }

        static void load(const CDFile& overlay_file) {
            load(overlay_file, nullptr, nullptr, 0ull);
        }

        template<size_t N>
        static void load(const CDFile& overlay_file, const volatile AutoLBAEntry* overlay_lba, const CDFile (&overlay_assets)[N]) {
            load(overlay_file, overlay_lba, overlay_assets, N);
        }

        void load_bios_info() {
            load(CDFileBuilder::overlay(LBA::BIOS_INFO_OVL, &__bios_info_start));
        }

        void load_controller_test() {
            load(CDFileBuilder::overlay(LBA::CONT_TEST_OVL, &__controller_tests_start), ControllerTest::lba, ControllerTest::Assets);
        }

        void load_gpu_test() {
            load(CDFileBuilder::overlay(LBA::GPU_TEST_OVL, &__gpu_tests_start), GPUTest::lba, GPUTest::Assets);
        }

        void load_large_gpu_test() {
            load(CDFileBuilder::overlay(LBA::GPU_TEST_OVL, &__gpu_tests_start), GPUTest::lba, GPUTest::LargeAssets);
        }

        void load_gte_test() {
            load(CDFileBuilder::overlay(LBA::GTE_TEST_OVL, &__gte_tests_start), GTETest::lba, GTETest::Assets);
        }

        void load_font_cycler() {
            load(CDFileBuilder::overlay(LBA::FONT_CYC_OVL, &__font_cycler_start));
        }

        void load_screen_center() {
            load(CDFileBuilder::overlay(LBA::SCREEN_CENT_OVL, &__screen_center_start), ScreenCenter::lba, ScreenCenter::Assets);
        }
    }

    namespace XAAudio {
        void play_mix() {
            CDXA::play(lba, MIX_XA, 0, false);
        }
    }
}