/*

  Rosegarden MIDI Sequencer

  File Name:	Record_SGI.c

  Description:	Sequencer Record functions using SGI dmedia library (flawed)

  Author:	Mostly Archer Sully

  SGI Fixes:	Reha Elci

*/


#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 <dmedia/midi.h>
#include <ulocks.h>

long	 RecordTempo         = 500000;
int      MidiEventBufferSize = 10000;

extern char *MidiPortName;

extern MDport MidiPort;
static MDport ThruPort;

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

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

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 * 1000L) / 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);
}


void Midi_SeqRecord(XtPointer, int *, XtInputId *);

/* for process sync. */
static char *tmplate = "/tmp/.roseXXXXX";
static char *fname;
static usptr_t *arena;
static usema_t *semap;


Boolean Midi_SeqInInit()
{
	char            ErrBuff[128];
	int             arg;
	int		OutputFlag;
	
	BEGIN("Midi_SeqInInit");

	OutputFlag = O_RDWR | O_NDELAY;


        arg = mdInit();
	printf("%d dev available %s\n", arg, mdGetName(0));
	if (arg <= 0) {
		sprintf(ErrBuff, "Unable to initialize Midi %s\n", MidiPortName);
		Error(NON_FATAL_REPORT_TO_MSGBOX, ErrBuff);
		RETURN_BOOL(False);
	}

	MidiPort = mdOpenInPort(0);
	ThruPort = mdOpenOutPort(0);
	mdSetStampMode(ThruPort, MD_NOSTAMP);

	if (MidiPort == NULL)
	{
		sprintf(ErrBuff, "Unable to open Midi Port %s\n", MidiPortName);
		Error(NON_FATAL_REPORT_TO_MSGBOX, ErrBuff);
		RETURN_BOOL(False);
	}

	if (fcntl(mdGetFd(MidiPort), F_SETFL, FNONBLOCK) < 0) {
		sprintf(ErrBuff, "Unable to set NONBLOCK mode %s\n", MidiPortName);
		Error(NON_FATAL_REPORT_TO_MSGBOX, ErrBuff);
		RETURN_BOOL(False);
	}

	mdSetStampMode(MidiPort, MD_RELATIVETICKS);
	mdSetDivision(MidiPort, 480);
	mdSetTempo(MidiPort, RecordTempo);

	fname = strdup(tmplate);
	mktemp(fname);
	arena = usinit(fname);
	unlink(fname);
	semap = usnewsema(arena, 0);

RETURN_BOOL(True);
}

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

	Recording = False;

	XtRemoveInput(eventCatcher);
        ret = Midi_SeqConvertRawDataToEventList();
	Midi_TrackListAddTrack(ret);

	mdClosePort(MidiPort);
	mdClosePort(ThruPort);
	XtDestroyWidget(StopRecordDlg);
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;

	if (!Midi_SeqInInit()) End;

	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);
	mdSetOrigin(MidiPort, 0);

        eventCatcher = XtAppAddInput(appContext,mdGetFd(MidiPort),(XtPointer)XtInputReadMask,Midi_SeqRecord,NULL);
END;
}

void Midi_SeqRecord(XtPointer a, int *b, XtInputId *c)
{
byte    LastStatus   = 0x90;
byte    NumOperands  = 0;
byte	OperandsLeft = 0;

byte    InByte;
byte   *BytePtr;
int     nevents;
char 	prbuf[1024];

int i;
struct tms ThrowAway;
MDevent InEv;
BEGIN("Midi_SeqRecord");

        nevents=mdReceive(MidiPort, &InEv, 1);
        if (nevents <= 0) return;
	while (Recording && nevents)
	{
/*
		mdPrintEvent(prbuf, &InEv, 1);
 		puts(prbuf);
*/
		NextEventPtr->Time = InEv.stamp;
		NextEventPtr->Bytes[0] = InEv.msg[0];
		OperandsLeft = mdMsgLen(InEv.msg[0]) - 1;
		BytePtr = &NextEventPtr->Bytes[1];

/* 		mdSend(ThruPort, &InEv, 1); */
		
		i = 1;
		while(OperandsLeft)
		{
			*BytePtr++ = InEv.msg[i++];
			--OperandsLeft;

		}				
         /* type cast note on with velocity 0 to note off */
         if (((NextEventPtr->Bytes[0]&0xf0) == MD_NOTEON) && (NextEventPtr->Bytes[2] == 0))
           NextEventPtr->Bytes[0] = (NextEventPtr->Bytes[0] & 0x0f) | MD_NOTEOFF;
         ++NextEventPtr;
	 if (nevents) nevents=mdReceive(MidiPort, &InEv, 1);
	 if (nevents <= 0) break;
	}
END;
}


