#include <PSX/System/IOPorts/spu_io.hpp>
#include <limits.hpp>

namespace JabyEngine {
    namespace boot {
        namespace SPU {
            using namespace JabyEngine;
            using namespace SPU_IO;
            using namespace SPU_IO_Values;
            
            static void clear_main_volume() {
                static constexpr auto StartVol = SweepVolume::from(SweepVolume::VolumeMode::Enable, SweepVolume::VolumeMode::Volume.with(SweepVolume::VolumeMode::MaxVolume >> 1));

                MainVolume::Left.write(StartVol);
                MainVolume::Right.write(StartVol);
            }

            static void clear_cd_and_ext_audio_volume() {
                CDVolume::Left.write(0.75_vol);
                CDVolume::Right.write(0.75_vol);

                ExternalAudioInputVolume::Left.write(SimpleVolume::mute());
                ExternalAudioInputVolume::Right.write(SimpleVolume::mute());
            }

            static void clear_voice() {
                for(auto& voice : SPU_IO::Voice) {
                    voice.volumeLeft.write(SweepVolume::mute());
                    voice.volumeRight.write(SweepVolume::mute());
                    voice.sampleRate.write(SampleRate::stop());
                    voice.ad.write(AD::none());
                    voice.sr.write(SR::none());
                    voice.adsr_volume.write(SimpleVolume::mute());
                    
                    voice.adr.write(SRAMAdr::adpcm_start());
                    voice.repeatAdr.write(SRAMAdr::adpcm_start());
                }
            }

            static void clear_pmon() {
                SPU_IO::PMON.write(PitchModulation::AllOff());
            }

            static void clear_noise_and_echo() {
                SPU_IO::NON.write(Noise::AllOff());
                SPU_IO::EON.write(Echo::AllOff());
            }

            static void clear_reverb() {
                Reverb::Volume::Left.write(SimpleVolume::mute());
                Reverb::Volume::Right.write(SimpleVolume::mute());
                Reverb::WorkAreaAdr.write(SRAMAdr::null());
            }

            static void setup_control_register() {
                static constexpr auto SetupValue = ControlRegister::from(ControlRegister::Enable, ControlRegister::Unmute, ControlRegister::CDAudioEnable);

                SPU_IO::ControlRegister.write(SetupValue);
            }

            static void wait_voices() {
                static constexpr auto Treshhold = SimpleVolume::mute();

                try_again:
                for(const auto& voice : SPU_IO::Voice) {
                    if(voice.adsr_volume.read() > Treshhold) {
                        goto try_again;
                    }
                }
            }

            void stop_voices() {
                SPU_IO::Key::Off.write(KeyOff::all());
            }

            void setup() {
                wait_voices();

                clear_main_volume();
                clear_cd_and_ext_audio_volume();
                clear_voice();
                clear_pmon();
                clear_noise_and_echo();
                clear_reverb();

                setup_control_register();
            }
        }
    }
}