#include "../../../internal-include/SPU/spu_internal.hpp"
#include <PSX/Auxiliary/big_endian.hpp>
#include <PSX/Auxiliary/word_helper.hpp>
#include <PSX/File/file_processor_helper.hpp>
#include <PSX/SPU/spu.hpp>
#include <stdio.hpp>

namespace JabyEngine {
    namespace FileProcessor {
        struct VAGHeader {
            char     id[4];
            uint32_t version;
            uint32_t reserved;
            uint32_t sample_size;
            uint32_t sample_frequency;
            uint8_t  reserved_2[12];
            char     name[16];

            constexpr uint32_t get_version() const {
                return read_be(this->version);
            }

            constexpr uint32_t get_sample_size() const {
                return read_be(this->sample_size);
            }

            constexpr uint32_t get_sample_frequency() const {
                return read_be(this->sample_frequency);
            }
        };

        struct VAGState {
            uint32_t          voice_id;
            size_t            words_left;
            SPU::SimpleVolume inital_vol;

            static constexpr VAGState create(uint32_t voice_id, SPU::SimpleVolume inital_vol) {
                return VAGState{.voice_id = voice_id, .words_left = 0, .inital_vol = inital_vol};
            }
        };

        static Progress parse_sample(State::CDDataProcessor& data_proc, VAGState& state) {
            const auto [words_to_use, is_last] = Helper::DMA::WordsReady::calculate(data_proc, state.words_left);
            const auto words_used              = Helper::DMA::send_words<SPU::internal::DMA>(words_to_use, is_last);

            state.words_left -= words_used;

            data_proc.processed(words_used*sizeof(uint32_t));
            return is_last ? Progress::Done : Progress::InProgress;
        }

        static Progress parse_header(State::CDDataProcessor& data_proc, VAGState& state) {
            if(data_proc.data_bytes >= sizeof(VAGHeader)) {
                const auto& header = *reinterpret_cast<const VAGHeader*>(data_proc.data_adr);
                const auto words   = bytes_to_words(header.get_sample_size());
                const auto bytes   = words_to_bytes(words);

                state.words_left = words;

                auto sram_adr = SPU::voice[state.voice_id].allocate(SPU_IO_Values::SampleRate::from_HZ(header.get_sample_frequency()), bytes);
                
                SPU::voice[state.voice_id].set_volume(state.inital_vol, state.inital_vol);

                data_proc.processed(sizeof(VAGHeader));
                SPU::internal::DMA::Receive::prepare();
                SPU::internal::DMA::Receive::set_dst(sram_adr);
                SPU::internal::DMA::Receive::set_src(reinterpret_cast<uintptr_t>(data_proc.data_adr));

                return Helper::exchange_and_execute_process_function(parse_sample, data_proc, state);
            }

            return Progress::InProgress;
        }

        State create(const uint32_t* data_adr, const VAG& file) {
            return State::from(VAGState::create(file.voice_number, file.inital_stereo_vol), data_adr, parse_header);
        }
    }
}