/*
Copyright (C) 2009 Devin Anderson

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#ifndef __JackPhysicalMidiInput__
#define __JackPhysicalMidiInput__

#include "JackMidiPort.h"
#include "ringbuffer.h"

namespace Jack {

    class JackPhysicalMidiInput {

    private:

        size_t buffered_bytes;
        size_t expected_data_bytes;
        jack_ringbuffer_t *input_ring;
        JackMidiBuffer *port_buffer;
        jack_midi_data_t status_byte;
        size_t unbuffered_bytes;

        void
        Clear();

        void
        WriteBufferedEvent(jack_nframes_t);

        void
        WriteBufferedSysexEvent(jack_nframes_t);

        void
        WriteByteEvent(jack_nframes_t, jack_midi_data_t);

    protected:

        /**
         * Override to specify how to react when 1 or more bytes of a MIDI
         * message are lost because there wasn't enough room in the input
         * buffer.  The first argument is the amount of bytes that couldn't be
         * buffered, and the second argument is the total amount of bytes in
         * the MIDI message.  The default implementation calls 'jack_error'
         * with a basic error message.
         */

        virtual void
        HandleBufferFailure(size_t, size_t);

        /**
         * Override to specify how to react when a new status byte is received
         * before all of the data bytes in a message are received.  The
         * argument is the number of bytes being discarded.  The default
         * implementation calls 'jack_error' with a basic error message.
         */

        virtual void
        HandleIncompleteMessage(size_t);

        /**
         * Override to specify how to react when an invalid status byte (0xf4,
         * 0xf5, 0xfd) is received.  The argument contains the invalid status
         * byte.  The default implementation calls 'jack_error' with a basic
         * error message.
         */

        virtual void
        HandleInvalidStatusByte(jack_midi_data_t);

        /**
         * Override to specify how to react when a sysex end byte (0xf7) is
         * received without first receiving a sysex start byte (0xf0).  The
         * argument contains the amount of bytes that will be discarded.  The
         * default implementation calls 'jack_error' with a basic error
         * message.
         */

        virtual void
        HandleUnexpectedSysexEnd(size_t);

        /**
         * Override to specify how to react when a MIDI message can not be
         * written to the port buffer.  The argument specifies the length of
         * the MIDI message.  The default implementation calls 'jack_error'
         * with a basic error message.
         */

        virtual void
        HandleWriteFailure(size_t);

        /**
         * This method *must* be overridden to handle receiving MIDI bytes.
         * The first argument is a pointer to the memory location at which the
         * MIDI byte should be stored.  The second argument is the last frame
         * at which a MIDI byte was received, except at the beginning of the
         * period when the value is 0.  The third argument is the total number
         * of frames in the period.  The return value is the frame at which the
         * MIDI byte is received at, or the value of the third argument is no
         * more MIDI bytes can be received in this period.
         */

        virtual jack_nframes_t
        Receive(jack_midi_data_t *, jack_nframes_t, jack_nframes_t) = 0;

    public:

        JackPhysicalMidiInput(size_t buffer_size=1024);
        ~JackPhysicalMidiInput();

        /**
         * Called to process MIDI data during a period.
         */

        void
        Process(jack_nframes_t);

        /**
         * Set the MIDI buffer that will receive incoming messages.
         */

        inline void
        SetPortBuffer(JackMidiBuffer *port_buffer)
        {
            this->port_buffer = port_buffer;
        }

    };

}

#endif
