/*
 * The pit_interrupt_service_routine function is responsible for playing music on the PC speaker.
 * It is an Interrupt Service Routine (ISR) that gets triggered by the Programmable Interval Timer
 * (PIT) interrupt (INT 0x1C).
 *
 * The function first checks if the ISR is enabled. If not, it returns immediately.
 * Then, it decreases the tick counter. If the tick counter is still greater than zero, it returns.
 * If the speaker is not off and there is a rest duration, it sets the tick counter to the rest 
 * duration, turns off the speaker, and returns.
 * 
 * The function then enters a loop where it reads the next note from memory. If the note duration 
 * is not zero, it processes the note. If the note is 0xFF, it sets the tick counter to the note 
 * duration, turns off the speaker, and returns. Otherwise, it sets the tick counter to the difference 
 * between the note duration and the rest duration, turns on the speaker with the frequency of the note, 
 * and returns.
 * 
 * If the note duration is zero, it checks if there are any repeats left. If not, it advances to the next sequence.
 * If there are no more sequences left, it turns off the speaker and returns.
 */                

union MusicPlayer {
    uint8_t unknown[0x10];
    struct {
        uint8_t _padding0[0x2];
        uint8_t isrEnabled;
    };
    struct {
        uint8_t _padding1[0x5];
        uint16_t currentSequenceAddressInMem;  // 0x5
        uint16_t currentNoteAddressInMem;      // 0x7
        uint8_t  repeats;                      // 0x9
        uint8_t  tickCounter;                  // 0xA
        uint8_t  noteOnDuration;               // 0xB
        uint8_t  restDuration;                 // 0xC
        uint8_t  speakerIsOff;                 // 0xD
        uint16_t freqValue; 
    };
};

void pit_interrupt_service_routine(union MusicPlayer* player) {

    if (!player->isrEnabled)
        return;

    --player->tickCounter;
    if (player->tickCounter > 0)
        return;

    if ((!player->speakerIsOff) && (player->restDuration != 0)) {
        player->tickCounter = player->restDuration;
        TurnOffPCSpeaker();
        player->speakerIsOff = 1;
        return;
    }

    for (;;) {
        uint16_t noteAddress = player->currentNoteAddressInMem;

        uint8_t noteDuration = mem[noteAddress++] & 0x7F;
        if (noteDuration != 0) {
            uint8_t restDuration = 0;
            if (noteDuration & 0x40) {
                noteDuration &= 0x3F;
                restDuration++;
            }
            player->noteOnDuration = noteDuration;
            player->restDuration = restDuration;
            uint8_t note = mem[noteAddress++];
            player->currentNoteAddressInMem = noteAddress;
            if (note == 0xFF) {
                player->tickCounter = player->noteOnDuration;
                TurnOffPCSpeaker();
                player->speakerIsOff = 1;
                return;
            }
            uint8_t tickDuration = player->noteOnDuration - player->restDuration;
            player->tickCounter = tickDuration;
            TurnOnPCSpeaker(frequencyLookupTable[note]);
            player->freqValue = 1193180 / frequencyLookupTable[note];
            player->speakerIsOff = 0;
            return;
        }

        
        uint16_t sequenceAddress = player->currentSequenceAddressInMem;
        --player->repeats;
        if (player->repeats == 0) {
            sequenceAddress += 3;
            uint8_t repeatCount = mem[sequenceAddress];
            if (repeatCount == 0) {
                player->isrEnabled = 0;
                return;
            }
            player->currentSequenceAddressInMem = sequenceAddress;
            player->repeats = repeatCount;
        }

        player->currentNoteAddressInMem = *(uint16_t*)&mem[sequenceAddress + 1];
        player->tickCounter = 1;
        player->speakerIsOff = 1;
    }
}