/*
---------------------------------------------------------------------------------------------
			MIDI Sequencer - Final Year Project, A.J. Green
---------------------------------------------------------------------------------------------

File Name:	Record.c

Description:	Sequencer Record functions.

Author:		AJG

History:

Update	Date		Programmer	Comments
======	====		==========	========
001	21/04/94	AJG		File Created.
002	17/02/95	cc		Added #ifdefs for HAVE_Z8530
003	24/05/95	cc		Archer Sully's SGI MIDI lib stuff

--------------------------------------------------------------------------------------------
*/

#undef POSIX_PLEASE
#undef _POSIX_SOURCE

#include "Record.h"

#include "Globals.h"
#include "Consts.h"

#include <MidiXInclude.h>
#include <MidiFile.h>
#include <MidiErrorHandler.h>
#include <Debug.h>
#include <MidiBHeap.h>
#include <MidiTrack.h>

#include "TrackList.h"

#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/times.h>
#include <unistd.h>
#include <sys/signal.h>

#include <sys/termio.h>
#include <sys/z8530.h>
#include <sys/stropts.h>

long	 RecordTempo         = 500000;
int      MidiEventBufferSize = 10000;

extern char *MidiPortName;

extern int   MidiPort;

typedef struct
{
	clock_t	Time;
	byte	Bytes[3];
}
MIDIRawEvent, *MIDIRawEventBuffer;

MIDIRawEventBuffer IncomingEventBuf;
MIDIRawEventBuffer NextEventPtr;
clock_t		   StartTime;
Widget		   StopRecordDlg;
Boolean		   Recording;
int		   MidiEventBufferTimer = 0;

EventList Midi_SeqConvertRawDataToEventlist(void);
void Midi_SeqRecord(void);


void Midi_SeqInInit(void)
{
char            ErrBuff[128];
struct termio   t;
struct strioctl str;
int             arg;

BEGIN("Midi_SeqInInit");

	MidiPort = open(MidiPortName, O_RDWR | O_NDELAY, 0666);


	if (MidiPort < 0)
	{
		sprintf(ErrBuff, "Unable to open Midi Port %s\n", MidiPortName);
		Error(NON_FATAL_REPORT_TO_MSGBOX, ErrBuff);
		END;
	}

	t.c_iflag = IGNBRK;
	t.c_oflag = 0;
	t.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
	t.c_lflag = 0;
	t.c_line = 1;
	t.c_cc[VINTR] = 0;
	t.c_cc[VQUIT] = 0;
	t.c_cc[VERASE] = 0;
	t.c_cc[VKILL] = 0;
	t.c_cc[VMIN] = 1;
	t.c_cc[VTIME] = 0;
	ioctl(MidiPort, TCSETAF, &t);


	if (ioctl(MidiPort, I_POP, 0) < 0)
	{
		Error(NON_FATAL_REPORT_TO_MSGBOX, "Unable to configure MIDI port");
		END;
	}

	str.ic_cmd = SIOC_RS422;
	str.ic_timout = 0;
	str.ic_len = 4;
	arg = RS422_ON;
	str.ic_dp = (char *)&arg;
	if (ioctl(MidiPort, I_STR, &str) < 0) 
	{
		Error(NON_FATAL_REPORT_TO_MSGBOX, "Can't ioctl RS422");
		END;
	}

	str.ic_cmd = SIOC_EXTCLK;
	str.ic_timout = 0;
	str.ic_len = 4;
	arg = EXTCLK_32X;
	str.ic_dp = (char *)&arg;
	if (ioctl(MidiPort, I_STR, &str) < 0) 
	{
		Error(NON_FATAL_REPORT_TO_MSGBOX, "Can't ioctl EXTCLK");
		END;
	}

	str.ic_cmd = SIOC_ITIMER;
	str.ic_timout = 0;
	str.ic_len = 4;
	arg = MidiEventBufferTimer;
	str.ic_dp = (char *)&arg;
	if (ioctl(MidiPort, I_STR, &str) < 0) 
	{
		Error(NON_FATAL_REPORT_TO_MSGBOX, "Can't ioctl ITIMER");
		END;
	}

END;
}

	
void Midi_SeqStopRecordingCB(Widget w, XtPointer a, XtPointer b)
{
BEGIN("Midi_SeqStopRecordingCB");

	Recording = False;

END;
}
	
void Midi_SeqReadTrack()
{
struct tms ThrowAway;
Widget     StopRecordButton;
XPoint     op;

BEGIN("Midi_SeqReadTrack");

	IncomingEventBuf = (MIDIRawEventBuffer)malloc(MidiEventBufferSize * sizeof(MIDIRawEvent));

	if (!IncomingEventBuf)
	{
		Error(NON_FATAL_REPORT_TO_MSGBOX, "Unable to allocate event buffer.");
		END;
	}

	NextEventPtr = IncomingEventBuf;

	Midi_SeqInInit();

	StopRecordDlg = XtCreatePopupShell("Stop Recording Dialogue", transientShellWidgetClass,
					   topLevel, NULL, 0);

	StopRecordButton = YCreateSurroundedWidget("STOP", commandWidgetClass, StopRecordDlg, 
						   SurroundShade, NoShade);

	XtAddCallback(StopRecordButton, XtNcallback, Midi_SeqStopRecordingCB, NULL);

	op = YPlacePopupAndWarp(StopRecordDlg, XtGrabNonexclusive,
				StopRecordButton, StopRecordButton);

	YAssertDialogueActions(StopRecordDlg, StopRecordButton,
			       StopRecordButton, NULL); 

	Recording = True;

	while(XtAppPending(appContext))
	{
		XtAppProcessEvent(appContext, XtIMAll);
	}

	Recording = True;
	StartTime = times(&ThrowAway);

	Midi_SeqRecord();

END;
}

EventList Midi_SeqConvertRawDataToEventList(void);


void Midi_SeqRecord(void)
{
byte    LastStatus   = 0x90;
byte    NumOperands  = 0;
byte	OperandsLeft = 0;

byte    InByte;
byte   *BytePtr;

struct tms ThrowAway;
BEGIN("Midi_SeqRecord");

	while(Recording)
	{
		while(!read(MidiPort, &InByte, 1));

		if (IsStatusByte(InByte))
		{
			if (MessageType(InByte) == MIDI_SYSTEM_MSG)
			{
				NumOperands = 0;
			}
			else if (MessageType(InByte) == MIDI_CTRL_CHANGE ||
				 MessageType(InByte) == MIDI_PROG_CHANGE)
			{
				NumOperands = 1;
			}
			else NumOperands = 2;
			LastStatus = InByte;
		}

		if (NumOperands)
		{
			NextEventPtr->Time = times(&ThrowAway) - StartTime;

			NextEventPtr->Bytes[0] = LastStatus;

			OperandsLeft = NumOperands;
			BytePtr = &NextEventPtr->Bytes[1];

			while(OperandsLeft)
			{
				while(!read(MidiPort, BytePtr, 1));
				if (!IsStatusByte(*BytePtr))
				{
					++BytePtr;
					--OperandsLeft;
				}
			}

			/*fprintf(stderr, "%ld %x %x %x\n", 
				NextEventPtr->Time,
				(int)NextEventPtr->Bytes[0],
				(int)NextEventPtr->Bytes[1],
				(int)NextEventPtr->Bytes[2]);*/
			++NextEventPtr;
		}				

		if (XtAppPending(appContext)) XtAppProcessEvent(appContext, XtIMAll);
	}

	Midi_TrackListAddTrack(Midi_SeqConvertRawDataToEventList());

	close(MidiPort);
	XtDestroyWidget(StopRecordDlg);

END;
}


EventList Midi_SeqConvertRawDataToEventList()
{
EventList          NewTrack, RunningPtr, OnePointTrack;
MIDIEventStruct    EventBuffer;
MIDIRawEventBuffer EventPtr;

BEGIN("Midi_SeqConvertRawDataToEventlist");

	NewTrack   = NULL;
	RunningPtr = NULL;

	EventPtr = IncomingEventBuf;

	if (MIDIHeaderBuffer.Format == MIDI_NO_FILE_LOADED) 
	{
		Midi_TrackListSetupDefaults();
	}
	while (EventPtr != NextEventPtr)
	{
		EventBuffer.DeltaTime = (((float)(EventPtr->Time * 10000L) / RecordTempo))
					 * MIDIHeaderBuffer.Timing.Division;
		EventBuffer.EventCode = EventPtr->Bytes[0];

		EventBuffer.EventData.NoteOn.Note     = EventPtr->Bytes[1];
		EventBuffer.EventData.NoteOn.Velocity = EventPtr->Bytes[2];

		if (!NewTrack)
		{
			NewTrack = Midi_EventCreateList(&EventBuffer, True);
			RunningPtr = NewTrack;
		}
		else
		{
			RunningPtr = (EventList)Nconc(RunningPtr, Midi_EventCreateList(&EventBuffer, True));
		}
		++EventPtr;
	}

	EventBuffer.EventCode = MIDI_FILE_META_EVENT;
	EventBuffer.EventData.MetaEvent.MetaEventCode = MIDI_END_OF_TRACK;
	EventBuffer.EventData.MetaEvent.NBytes = 0;

	if (!NewTrack)
	{
		NewTrack = Midi_EventCreateList(&EventBuffer, True);
		RunningPtr = NewTrack;
	}
	else
	{
		RunningPtr = (EventList)Nconc(RunningPtr, Midi_EventCreateList(&EventBuffer, True));
	}

	Midi_TrackConvertToOnePointRepresentation(NewTrack);

RETURN_PTR(NewTrack);
}


