#ifndef STD_H
#include "std.h"
#endif

#ifndef MPU_H
#define MPU_H
// Manifest constants

#define MAX_MPU_TRACK 7	// Maximum number of MPU play tracks available
								// NOTE: Mpu supports up to 8 tracks, but track 8
								// is used to send unscheduled midi messages, so this
								// number can't exceed 7! -rd


// MPU Module Error Codes
typedef enum {
	MPU_OK = 0,
	MPU_NOMEM,		// Insufficient memory to run
	MPU_EHARDWARE,  // Hardware problem (interrupts?)
	MPU_NOHARDWARE, // Hardware not detected
	MPU_NOINTERRUPTHANDLERS, // Too many open MpuPortT's.
	MPU_OVERRUN,  	// Receive overrun
} MpuErrorT;

// Error strings to match (PRIVATE)
#ifdef MPU_PRIVATES
PRIVATE char *mpuErrorStrings[] = {
	"Successful",
	"Insufficient memory",
	"MPU-401 Hardware error",
	"Unable to detect MPU-401",
	"Too many installed interrupt handlers",
	"Read overrun"
};
#endif

// Mpu Mode bits
#define MPU_VOICES_THRU     0x02
#define MPU_EXCLUSIVE_THRU  0x04
#define MPU_REALTIME_THRU   0x08
#define MPU_COMMON_THRU     0x10

#define MPU_DEFAULT_THRU (MPU_VOICES_THRU)
#define MPU_NONE_THRU		 0x00

#define MPU_RX_EXCLUSIVE		0x20
#define MPU_RX_COMMON           0x40
#define MPU_RX_REALTIME         0x80
#define MPU_RX_BENDER          0x100
#define MPU_RX_MODE            0x200

#define MPU_RX_DEFAULT		   0x00
#define MPU_RX_ALL			   0x3E0

typedef enum MpuClockT {
	MPU_INTERNAL_CLOCK,
	MPU_MIDI_CLOCK,
	MPU_FSK_CLOCK
} MpuClockT;

typedef enum MpuTimebaseT {
	MPU_TIMEBASE_48,
	MPU_TIMEBASE_72,
	MPU_TIMEBASE_96,
	MPU_TIMEBASE_120,
	MPU_TIMEBASE_144,
	MPU_TIMEBASE_168,
	MPU_TIMEBASE_192
} MpuTimebaseT;

typedef enum MpuOperatingModeT {
	RECORD_MODE,	// Read messages w/ timestamps
	PLAY_MODE,		// Write scheduled messages
	RECORDPLAY_MODE, // Read messages w/ timestamps, write scheduled messages
	STOP_MODE   	// Stop
} MpuOperatingModeT;



// Mpu Port Control structure
typedef struct MpuPortT {
	int mpuErrno;
	int dataPort;
	int commandPort;
	int statusPort;
	int mpuInterrupt;
	int dataHead;
	int dataTail;
	int bufferSize;
	int currentMode;
	UCHAR *readBuffer;
	void interrupt (*oldInterrupt)();
	int ungetChar;
	long currentTime;
	int lastCommand;
	/* Interrupt handler data */
	BOOL _inSysex;
	void (*trackRequestHandler)(void *,int );
	void *requestHandlerData;

	BOOL overrunError;
} MpuPortT;

// Function Prototypes

int GetMpuStatus(MpuPortT *mpu);
char *MpuErrorString(int mpuErrno);

MpuPortT *CreateMpuPort(
	int base_address,    	// Mpu Base address (default 0x330)
	int interrupt_number,   // Mpu Interrupt (default 2)
	int buffer_size,		// Receive buffer size (default 1024)
	enum MpuOperatingModeT operating_mode, // Operating mode:
							 // RECORD_MODE,
							 // PLAY_MODE,
							 // READWRITE_MODE
	enum MpuClockT clock_source, // 	MPU_INTERNAL_CLOCK
								 // or	MPU_MIDI_CLOCK
								 // or	MPU_FSK_CLOCK
	int tempo,					// Beats per minute
	enum MpuTimebaseT timebase, // Ticks per beat (see MpuTimebaseT in MPU.H)
	int metronome_measure_length, // beats per measure,0-> metronome off
	int mode,				// See DESCRIPTION
	int midi_channel_mask,  // bit n controls midi channel n+1
							// bit = 0 -> pass through without host intervention
							// bit = 1 -> record/filter this midi channel
	int *result				// retcode is placed here
);

BOOL DestroyMpu(MpuPortT *mpu);

int SendCommand(MpuPortT *mpu,UCHAR cmd);
int SendData(MpuPortT *mpu,UCHAR data);

int ReadData(MpuPortT *mpu);
void UngetData(MpuPortT *mpu,UCHAR data);

void SetMpuOperatingMode(
	MpuPortT *port,
	MpuOperatingModeT operating_mode // e.g. RECORD_MODE, PLAY_MODE, STOP_MODE
);

#endif
