#include "../../internal-include/CD/cd_internal.hpp"
#include <PSX/System/IOPorts/dma_io.hpp>

namespace JabyEngine {
    namespace CDDA {
        extern CD::internal::BCDTimeStamp playing_track;
    }

    namespace CDXA {
        CD::internal::State interrupt_handler(uint8_t irq);
    }

    namespace CD {
        namespace internal {
            SectorBufferAllocator sector_allocator;
            File                  cur_file;

            namespace IRQ {
                static void process(uint32_t irq) {
                    if(current_state != State::XAMode) {
                        switch(irq) {
                            case CD_IO::Interrupt::DataReady: {
                                // Obtain sector content here
                                auto* sector = sector_allocator.allocate_sector();
                                if(sector) {
                                    //Now obtain sector
                                    copy_from_sector(sector->data, CD_IO::DataSector::SizeBytes);
                                    
                                    if(cur_file.done_processing()) {
                                        current_state = State::Done;
                                        Command::send_no_wait(CD_IO::Command::Pause);
                                    }
                                }

                                else {
                                    current_state = State::BufferFull;
                                    Command::send_no_wait(CD_IO::Command::Pause);
                                }
                            } break;

                            case CD_IO::Interrupt::DataEnd: {
                                set_loc(CDDA::playing_track);
                                Command::send_no_wait(CD_IO::Command::Play);
                            } break;

                            case CD_IO::Interrupt::DiskError: {
                                current_state = State::Error;
                            } break;                                
                        }
                    }

                    else {
                        current_state = CDXA::interrupt_handler(irq);
                    }
                }

                //######################################################################################################################

                SysCall::InterruptVerifierResult verifier() {
                    if(Interrupt::is_irq(Interrupt::CDROM)) {
                        Interrupt::ack_irq(Interrupt::CDROM);
                        return SysCall::InterruptVerifierResult::ExecuteHandler;
                    }

                    else {
                        return SysCall::InterruptVerifierResult::SkipHandler;
                    }
                }

                void handler(uint32_t x) {
                    const auto old_status  = CD_IO::IndexStatus.read();

                    CD_IO::PortIndex1::change_to();
                    const auto cur_irq = CD_IO::Interrupt::get_type(CD_IO::PortIndex1::InterruptFlag);
                    CD_IO::PortIndex1::change_to();
                    CD_IO::Interrupt::ack_extended(CD_IO::PortIndex1::InterruptFlag);

                    irq_bit_pending = bit::clear(irq_bit_pending, cur_irq);
                    
                    CD_IO::PortIndex0::change_to();
                    if(cur_irq == CD_IO::Interrupt::DataReady) {
                        CD_IO::PortIndex0::Request.write(CD_IO_Values::Request::want_data());
                    }

                    process(cur_irq);
                    // No masking required because we can only write bit 0 - 2
                    CD_IO::IndexStatus.write(old_status);
                    return SysCall::ReturnFromException();
                }
            }
        }
    }
}