#pragma once
#include "ioport.hpp"
#include <limits.hpp>

namespace JabyEngine {
    namespace SPU_IO_Values {
        namespace MemoryMap {
            static constexpr uintptr_t ADPCM = 0x01000;
            static constexpr uintptr_t End   = 0x7FFFF;
        }

        __declare_io_struct(AD, uint16_t) {
            static constexpr auto AttackMode   = Bit(15);
            static constexpr auto AttackShift  = BitRange::from_to(10, 14);
            static constexpr auto AttackStep   = BitRange::from_to(8, 9);
            static constexpr auto DecayShift   = BitRange::from_to(4, 7);
            static constexpr auto SustainLevel = BitRange::from_to(0, 3);

            static constexpr AD none() {
                return AD{0};
            }
        };

        __declare_io_struct(ControlRegister, uint16_t) {
            enum RAMTransferMode {
                Stop        = 0,
                ManualWrite = 1,
                DMAWrite    = 2,
                DMARead     = 3
            };

            static constexpr auto Enable              = Bit(15);
            static constexpr auto Unmute              = Bit(14);
            static constexpr auto NoiseFrequcenyShift = BitRange::from_to(10, 13);
            static constexpr auto NoiseFrequcenyStep  = BitRange::from_to(8, 9);
            static constexpr auto ReverbMasterEnable  = Bit(7);
            static constexpr auto IRQ9Enable          = Bit(6);
            static constexpr auto TransferMode        = BitRange::from_to(4, 5);
            static constexpr auto ExternalAudioReverb = Bit(3);
            static constexpr auto CDAudioReverb       = Bit(2);
            static constexpr auto ExternalAudioEnable = Bit(1);
            static constexpr auto CDAudioEnable       = Bit(0);
        };

        __declare_io_struct(DataTransferControl, uint16_t) {
            static constexpr DataTransferControl NormalTransferMode() {
                return DataTransferControl{0x0004};
            }
        };

        __declare_io_struct(Echo, uint32_t) {
            static constexpr auto EchoBits = BitRange::from_to(0, 23);

            static constexpr Echo AllOff() {
                return Echo{0};
            }
        };

        __declare_io_struct(KeyOff, uint32_t) {
            static constexpr KeyOff for_specific(uint32_t id) {
                return KeyOff{1u << id};
            }

            static constexpr KeyOff all() {
                return KeyOff{UI32_MAX};
            }
        };

        __declare_io_struct(KeyOn, uint32_t) {
            static constexpr KeyOn for_specific(uint32_t id) {
                return KeyOn{1u << id};
            }

            static constexpr KeyOn all() {
                return KeyOn{UI32_MAX};
            }
        };

        __declare_io_struct(KeyStatus, uint32_t) {
        };

        __declare_io_struct(Noise, uint16_t) {
            static constexpr auto NoiseBits = BitRange::from_to(0, 23);

            static constexpr Noise AllOff() {
                return Noise{0};
            }
        };

        __declare_io_struct(PitchModulation, uint32_t) {
            static constexpr auto EnableBits = BitRange::from_to(1, 23);

            static constexpr PitchModulation AllOff() {
                return PitchModulation{0};
            }
        };

        __declare_io_struct(SampleRate, uint16_t) { 
            static constexpr SampleRate stop() {
                return SampleRate{0};
            }

            static constexpr SampleRate from_HZ(uint32_t freq) {
                constexpr uint32_t Base1024Hz = static_cast<uint32_t>((4096.0/44100.0)*1024.0);
                return SampleRate{static_cast<uint16_t>((freq >> 10)*Base1024Hz)};
            }

            static constexpr SampleRate from_HZ(double freq) {
                //4096 == 44100Hz
                constexpr double Base = (4096.0 / 44100.0);
                return SampleRate{static_cast<uint16_t>((freq*Base))};
            }
        };

        __declare_io_struct(SimpleVolume, int16_t) {
            static constexpr auto MaxVolume = I16_MAX;

            static constexpr SimpleVolume mute() {
                return SimpleVolume{0};
            }

            constexpr operator int16_t() const {
                return this->raw;
            }
        };

        static constexpr SimpleVolume operator""_vol(long double fraction) {
            return {static_cast<int16_t>(static_cast<long double>(SimpleVolume::MaxVolume)*fraction)};
        }

        __declare_io_struct(SR, uint16_t) {
            static constexpr auto SustainMode      = Bit(31 - 16);
            static constexpr auto SustainDirection = Bit(30 - 16);
            static constexpr auto SustainShift     = BitRange::from_to((24 - 16), (28 - 16));
            static constexpr auto SustainStep      = BitRange::from_to((22 - 16), (23 - 16));
            static constexpr auto ReleaseMode      = Bit(21 - 16);
            static constexpr auto ReleaseShift     = BitRange::from_to((16 - 16), (20 - 16));

            static constexpr SR none() {
                return SR{0};
            }
        };

        __declare_io_struct(SRAMAdr, uint16_t) {
            static constexpr SRAMAdr null() {
                return SRAMAdr{0x0};
            }

            static constexpr SRAMAdr adpcm_start() {
                return SRAMAdr{MemoryMap::ADPCM};
            }
        };

        __declare_io_struct(StatusRegister, uint16_t) {
            enum CapureBufferHalf {
                First  = 0,
                Second = 1
            };

            static constexpr auto Unused              = BitRange::from_to(12, 15);
            static constexpr auto CaputreBufferHalf   = Bit(11);
            static constexpr auto TransferBusy        = Bit(10);
            static constexpr auto IsDMARead           = Bit(9);
            static constexpr auto isDMAWrite          = Bit(8);
            static constexpr auto isDMA               = Bit(7);
            static constexpr auto isIRQ               = Bit(6);
            // Copies of ControlRegister
            static constexpr auto TransferMode        = BitRange::from_to(4, 5);
            static constexpr auto ExternalAudioReverb = Bit(3);
            static constexpr auto CDAudioReverb       = Bit(2);
            static constexpr auto ExternalAudioEnable = Bit(1);
            static constexpr auto CDAudioEnable       = Bit(0);
        };
        
        __declare_io_struct(SweepVolume, int16_t) {
            struct VolumeMode {
                static constexpr auto MaxVolume   = (I16_MAX >> 1);
                static constexpr auto EnableSweep = Bit(15);
                static constexpr auto Enable      = !EnableSweep;
                static constexpr auto Volume      = BitRange::from_to(0, 14);                
            };

            struct SweepMode {
                enum Mode {
                    Linear      = 0,
                    Exponential = 1,
                };

                enum Direction {
                    Increase = 0,
                    Decrease = 1,
                };

                enum Phase {
                    Posititve = 0,
                    Negative  = 1,
                };

                static constexpr auto Mode      = Bit(14);
                static constexpr auto Direction = Bit(13);
                static constexpr auto Phase     = Bit(12);
                static constexpr auto Shift     = BitRange::from_to(2, 6);
                static constexpr auto Step      = BitRange::from_to(0, 1);
            };

            static constexpr SweepVolume create(SimpleVolume volume) {
                return from(VolumeMode::Enable, VolumeMode::Volume.with(volume.raw >> 1));
            }

            static constexpr SweepVolume mute() {
                return SweepVolume{0};
            }
        };
    }
}