// Each MIDIPacket can contain more than one midi messages.
// This function processes the packet and adds the messages to the specified message queue.
// @see also src/share/native/com/sun/media/sound/PlatformMidi.h.
static void processMessagesForPacket(const MIDIPacket* packet, MacMidiDeviceHandle* handle) {
    const UInt8* data;
    UInt16 length;
    UInt8 byte;
    UInt8 pendingMessageStatus;
    UInt8 pendingData[2];
    UInt16 pendingDataIndex, pendingDataLength;
    UINT32 packedMsg;
    MIDITimeStamp ts = packet->timeStamp;

    pendingMessageStatus = 0;
    pendingDataIndex = pendingDataLength = 0;

    data = packet->data;
    length = packet->length;
    while (length--) {
        bool byteIsInvalid = FALSE;
        
        byte = *data++;
        packedMsg = byte;

        if (byte >= 0xF8) {
            // Each RealTime Category message (ie, Status of 0xF8 to 0xFF) consists of only 1 byte, the Status.
            // Except that 0xFD is an invalid status code.
            //
            // 0xF8 -> Midi clock
            // 0xF9 -> Midi tick
            // 0xFA -> Midi start
            // 0xFB -> Midi continue
            // 0xFC -> Midi stop
            // 0xFE -> Active sense
            // 0xFF -> Reset
            if (byte == 0xFD) {
                byteIsInvalid = TRUE;
            } else {
                pendingDataLength = 0;
            }
        } else {
            if (byte < 0x80) {
                // Not a status byte -- check our history.
                if (handle->readingSysExData) {
                    CFDataAppendBytes(handle->readingSysExData, &byte, 1);

                } else if (pendingDataIndex < pendingDataLength) {
                    pendingData[pendingDataIndex] = byte;
                    pendingDataIndex++;

                    if (pendingDataIndex == pendingDataLength) {
                        // This message is now done -- do the final processing.
                        if (pendingDataLength == 2) {
                            packedMsg = pendingMessageStatus | pendingData[0] << 8 | pendingData[1] << 16;
                        } else if (pendingDataLength == 1) {
                            packedMsg = pendingMessageStatus | pendingData[0] << 8;
                        } else {
                            fprintf(stderr, "%s: %d->internal error: pendingMessageStatus=0x%X, pendingDataLength=%d\n",
                                    __FILE__, __LINE__, pendingMessageStatus, pendingDataLength);
                            byteIsInvalid = TRUE;
                        }
                        pendingDataLength = 0;
                    }                    
                } else {
                    // Skip this byte -- it is invalid.
                    byteIsInvalid = TRUE;
                }
            } else {
                if (handle->readingSysExData /* && (byte == 0xF7) */) {
                    // We have reached the end of system exclusive message -- send it finally.
                    const UInt8* bytes = CFDataGetBytePtr(handle->readingSysExData);
                    CFIndex size = CFDataGetLength(handle->readingSysExData);
                    MIDI_QueueAddLong(handle->h.queue,
                                      (UBYTE*) bytes,
                                      (UINT32) size,
                                      0, // Don't care, windowish porting only.
                                      (INT64) (AudioConvertHostTimeToNanos(ts) + 500) / 1000,
                                      TRUE);
                    CFRelease(handle->readingSysExData);
                    handle->readingSysExData = NULL;
                }

                pendingMessageStatus = byte;
                pendingDataLength = 0;
                pendingDataIndex = 0;
                
                switch (byte & 0xF0) {
                    case 0x80:    // Note off
                    case 0x90:    // Note on
                    case 0xA0:    // Aftertouch
                    case 0xB0:    // Controller
                    case 0xE0:    // Pitch wheel
                        pendingDataLength = 2;
                        break;

                    case 0xC0:    // Program change
                    case 0xD0:    // Channel pressure
                        pendingDataLength = 1;
                        break;

                    case 0xF0: {
                        // System common message
                        switch (byte) {
                        case 0xF0:
                            // System exclusive
                            // Allocates a CFMutableData reference to accumulate the SysEx data until EOX (0xF7) is reached.
                            handle->readingSysExData = CFDataCreateMutable(NULL, 0);
                            break;
                            
                        case 0xF7:
                            // System exclusive ends--already handled above.
                            // But if this is showing up outside of sysex, it's invalid.
                            byteIsInvalid = TRUE;
                            break;
                            
                        case 0xF1:    // MTC quarter frame message
                        case 0xF3:    // Song select
                            pendingDataLength = 1;
                            break;
                            
                        case 0xF2:    // Song position pointer
                            pendingDataLength = 2;
                            break;
                            
                        case 0xF6:    // Tune request
                            pendingDataLength = 0;
                            break;
                            
                        default:
                            // Invalid message
                            byteIsInvalid = TRUE;
                            break;
                        }
                        break;
                    }
                        
                    default:
                        // This can't happen, but handle it anyway.
                        byteIsInvalid = TRUE;
                        break;
                }
            }
        }
        if (byteIsInvalid) continue;

        // If the byte is valid and pendingDataLength is 0, we are ready to send the message.
        if (pendingDataLength == 0) {
            MIDI_QueueAddShort(handle->h.queue, packedMsg, (INT64) (AudioConvertHostTimeToNanos(ts) + 500) / 1000, TRUE);
        }
    }

}
//$$fb dwParam1 holds a pointer for long messages. How can that be a DWORD then ???
void CALLBACK MIDI_IN_PutMessage( HMIDIIN hMidiIn, UINT wMsg, UINT_PTR dwInstance, UINT_PTR dwParam1, UINT_PTR dwParam2 ) {

    MidiDeviceHandle* handle = (MidiDeviceHandle*) dwInstance;

    TRACE3("> MIDI_IN_PutMessage, hMidiIn: %x, wMsg: %x, dwInstance: %x\n", hMidiIn, wMsg, dwInstance);
    TRACE2("                      dwParam1: %x, dwParam2: %x\n", dwParam1, dwParam2);

    switch(wMsg) {

    case MIM_OPEN:
        TRACE0("< MIDI_IN_PutMessage: MIM_OPEN\n");
        break;

    case MIM_CLOSE:
        TRACE0("< MIDI_IN_PutMessage: MIM_CLOSE\n");
        break;

    case MIM_MOREDATA:
    case MIM_DATA:
        TRACE3("  MIDI_IN_PutMessage: MIM_MOREDATA or MIM_DATA. status=%x  data1=%x  data2=%x\n",
               dwParam1 & 0xFF, (dwParam1 & 0xFF00)>>8, (dwParam1 & 0xFF0000)>>16);
        if (handle!=NULL && handle->queue!=NULL && handle->platformData) {
            MIDI_QueueAddShort(handle->queue,
                               // queue stores packedMsg in big endian
                               //(dwParam1 << 24) | ((dwParam1 << 8) & 0xFF0000) | ((dwParam1 >> 8) & 0xFF00),
                               (UINT32) dwParam1,
                               // queue uses microseconds
                               ((INT64) dwParam2)*1000,
                               // overwrite if queue is full
                               TRUE);
            SetEvent((HANDLE) handle->platformData);
        }
        TRACE0("< MIDI_IN_PutMessage\n");
        break;

    case MIM_LONGDATA:
        TRACE1("  MIDI_IN_PutMessage: MIM_LONGDATA (%d bytes recorded)\n", (int) (((MIDIHDR*) dwParam1)->dwBytesRecorded));
        if (handle!=NULL && handle->queue!=NULL && handle->platformData) {
            MIDIHDR* hdr = (MIDIHDR*) dwParam1;
            TRACE2("  MIDI_IN_PutMessage: Adding to queue: index %d, %d bytes\n", (INT32) hdr->dwUser, hdr->dwBytesRecorded);
            MIDI_QueueAddLong(handle->queue,
                              (UBYTE*) hdr->lpData,
                              (UINT32) hdr->dwBytesRecorded,
                              // sysex buffer index
                              (INT32) hdr->dwUser,
                              // queue uses microseconds
                              ((INT64) dwParam2)*1000,
                              // overwrite if queue is full
                              TRUE);
            SetEvent((HANDLE) handle->platformData);
        }
        TRACE0("< MIDI_IN_PutMessage\n");
        break;

    case MIM_ERROR:
        ERROR0("< MIDI_IN_PutMessage: MIM_ERROR!\n");
        break;

    case MIM_LONGERROR:
        if (dwParam1 != 0) {
            MIDIHDR* hdr = (MIDIHDR*) dwParam1;
#ifdef USE_TRACE
            if (hdr->dwBytesRecorded > 0) {
                TRACE2("  MIDI_IN_PutMessage: MIM_LONGERROR! recorded: %d bytes with status 0x%2x\n",
                        hdr->dwBytesRecorded, (int) (*((UBYTE*) hdr->lpData)));
            }
#endif
            // re-add hdr to device query
            hdr->dwBytesRecorded = 0;
            midiInAddBuffer((HMIDIIN)handle->deviceHandle, hdr, sizeof(MIDIHDR));
        }
        ERROR0("< MIDI_IN_PutMessage: MIM_LONGERROR!\n");
        break;

    default:
        ERROR1("< MIDI_IN_PutMessage: ERROR unknown message %d!\n", wMsg);
        break;

    } // switch (wMsg)
}