/* $Id: amigmach.c,v 1.6 89/05/06 17:13:10 lee Exp $
 *
 * GLIB - a Generic LIBrarian and editor for synths
 *
 * Machine dependent stuff.
 *
 * Amiga version.
 * Alan Bland
 *
 * modifications by Mark Rinfret and Dave Weiler
 * $Log:	amigmach.c,v $
 * Revision 1.6  89/05/06  17:13:10  lee
 * rel. to comp.sources.misc
 * 
 */

#include "glib.h"
#include <stdio.h>
#include <ctype.h>

#ifndef AZTEC_C
#include <dos.h>
#endif
#include <intuition/intuition.h>
#include <exec/types.h>
#include <exec/ports.h>
#include <exec/io.h>
#include <exec/devices.h>
#include <exec/memory.h>
#include <devices/serial.h>

#ifdef ARP
	#include <libraries/arpbase.h>
	#include <arpfunctions.h>
#endif

#ifdef AZTEC_C
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <functions.h>
#else
#include <proto/exec.h>
#include <proto/intuition.h>
#endif

#ifdef ARP
	char FileName[FCHARS+1];
	char DirName[DSIZE+1];
	struct FileRequester MyFileRequest =
		{
		"GLIB file name -->",
		FileName,
		DirName,
		0,
		0,0,
		0,
		0
		};
	int FileRequestImminent;
#else
	struct IntuitionBase *IntuitionBase;
	struct GfxBase *GfxBase;
#endif

struct Screen *S;
struct Window *W;
struct RastPort *R;

int Rows, Cols;

#define TOPEDGE 8	/* how much of screen bar to show */
int WindowWidth = 640;
int WindowHeight = 200 - TOPEDGE;

/* standard WB colors */
#define BLUE 0
#define WHITE 1
#define BLACK 2
#define ORANGE 3


hello()
{
	windinit();
	openmidi();
}

bye()
{
	closemidi();
	windexit(0);
}

int MouseX, MouseY, MouseButtons;
int Mouseok = 0;

/* getmousepos - get current row and column of mouse */
getmousepos(amr,amc)
int *amr;
int *amc;
{
	*amr = MouseY / 8;
	*amc = MouseX / 8;
}

/* statmouse - return mouse button state (0=nothing pressed,1=left,2=right) */
statmouse()
{
	nextevent(MOUSEBUTTONS, 0);
	return MouseButtons;
}

/* Return when either a console key or mouse button is pressed. */
mouseorkey()
{
	return nextevent(MOUSEBUTTONS | VANILLAKEY, 1);
}

flushconsole()
{
	while (nextevent(VANILLAKEY, 0) >= 0) ;
}

getconsole()
{
	return nextevent(VANILLAKEY, 1);
}

cursor(state)
int state;
{
	int x, y;
	x = R->cp_x;
	y = R->cp_y - R->Font->tf_Baseline;
	SetDrMd(R, COMPLEMENT);
	RectFill(R, x, y, x+8, y+R->Font->tf_YSize-1);
	SetDrMd(R, JAM2);
}

nextevent(flags, wait)
long flags;
int wait;
{
	register int class, code;
	register struct IntuiMessage *message;
	int result;

	cursor(1);
	ModifyIDCMP(W, CLOSEWINDOW | flags);
	while (1)
	{
		if (wait)
		{
		    /* get next event, waiting if none are available */
		    while ((message = (struct IntuiMessage *)GetMsg(W->UserPort)) == NULL)
		    {
			Wait(1<<W->UserPort->mp_SigBit);
		    }
		}
		else
		{
		    /* get next event, but return if none are available */
		    message = (struct IntuiMessage *)GetMsg(W->UserPort);
		    if (message == NULL)
		    {
			result = -1;
			break;
		    }
		}
		class = message->Class;
		code = message->Code;
		MouseX = message->MouseX;
		MouseY = message->MouseY;
		ReplyMsg((struct Message *)message);

		switch (class)
		{
		case VANILLAKEY:
			result = code;
			break;
		case CLOSEWINDOW:
			result = 'q';
			break;
		case MOUSEBUTTONS:
			switch (code)
			{
			case SELECTDOWN:
				MouseButtons = 1;
				break;
			case MENUDOWN:
				MouseButtons = 2;
				break;
			default:
				MouseButtons = 0;
				break;
			}
			result = MOUSE;
			break;
		default:
			continue;
		}
		break;
	}
	cursor(0);
	return result;
}

/*------------------------------------------------------------------------
 * MIDI I/O Routines for Amiga.
 *
 * Uses low-level serial I/O for simultaneous reads and writes.
 */

struct MsgPort	*MidiInPort, *MidiOutPort;
struct IOExtSer	*MidiIn, *MidiOut;
int	SerOpen = 0;

unsigned char   MidiInBuf;
int	MidiDataAvail = 0;

/*
 * start an asynchronous read request from MIDI IN
 */

startmidiread()
{
	MidiIn->IOSer.io_Data = (APTR) &MidiInBuf;
	MidiIn->IOSer.io_Length = 1;
	MidiIn->IOSer.io_Command = CMD_READ;
	MidiIn->IOSer.io_Flags = IOF_QUICK;	/* use quick I/O */
	BeginIO((struct IORequest *) MidiIn);
	/* did I/O complete quickly? */
	if ((MidiIn->IOSer.io_Flags & IOF_QUICK)) {
		/* wow, data's coming in fast! */
		MidiDataAvail = 1;
	} else {
		/* no data this time, it'll arrive later */
		MidiDataAvail = 0;
	}
}

/*
 * get a byte from MIDI IN.
 * assumes startmidiread has been previously done.  if statmidi has been
 * checked, this function will not wait.  otherwise, it will wait for
 * a single byte to arrive if none is available.
 */
unsigned
getmidi()
{
	register struct Message	*io;
        unsigned result;

	/* return previously-received data, if available */
	if (MidiDataAvail) {
                result = MidiInBuf & 0xff;
		/* start a new I/O */
		startmidiread();
		return result;
	}

	/* read next available byte */
	io = (struct Message *) CheckIO((struct IORequest *) MidiIn);
	if (io == FALSE) {
		/* wait for next byte */
		WaitIO((struct IORequest *) MidiIn);
		io = &MidiIn->IOSer.io_Message;
	}

	Remove(&io->mn_Node);
	result = MidiInBuf;
	startmidiread();	/* start I/O for next byte */
	return result;
}

/*
 * write one byte to MIDI OUT
 */

sendmidi(c)
int c;
{
	char	buf = c;
#ifdef STATISTICS
        long    bytesOut;
        long    start, totalTime, average;


        ++bytesOut;
        start = milliclock();
#endif
	MidiOut->IOSer.io_Data = (APTR) &buf;
	MidiOut->IOSer.io_Length = 1;
	MidiOut->IOSer.io_Command = CMD_WRITE;
	DoIO((struct IORequest *) MidiOut);	/* synchronous request */
#ifdef STATISTICS
        totalTime += milliclock() - start;
        average = totalTime / bytesOut;
#endif
}

/*  FUNCTION
        sendmulti - send multiple bytes to midi device

    SYNOPSIS
        void sendmulti(buffer, count)
                char    *buffer;
                int     count;

    DESCRIPTION
        sendmulti transfers <count> bytes from <buffer> to the currently
        open midi device.

*/

void
sendmulti(buffer, count)
    char *buffer; int count;
{
#ifdef STATISTICS
        long    bytesOut;
        long    start, totalTime, average;


        bytesOut += count;
        start = milliclock();
#endif
        MidiOut->IOSer.io_Data = (APTR) buffer;
        MidiOut->IOSer.io_Length = count;
        MidiOut->IOSer.io_Command = CMD_WRITE;
        DoIO((struct IORequest *) MidiOut);     /* synchronous request */
#ifdef STATISTICS
        totalTime += milliclock() - start;
        average = totalTime / bytesOut;
#endif


}

/*
 * check if any midi data is waiting to be received
 */
statmidi()
{
	/* check if data has previously been received */
	if (MidiDataAvail) return 1;

	/* check if i/o has completed */
	return (CheckIO((struct IORequest *) MidiIn) == FALSE) ? 0 : 1;
}

openmidi()
{
	/* create message port for serial device */
        MidiInPort = (struct MsgPort *) CreatePort(SERIALNAME,0L);
	if (MidiInPort == NULL) fatal("Can't create MidiInPort");

	/* create i/o request block for serial device */
	MidiIn = (struct IOExtSer *)
                CreateExtIO(MidiInPort, (long) sizeof(struct IOExtSer));
	if (MidiIn == NULL) fatal("Can't create MidiIn");

	/* open the serial device */
	MidiIn->io_SerFlags = SERF_SHARED;
	SerOpen = OpenDevice(SERIALNAME,0,(struct IORequest *) MidiIn,0) == 0 ? 1 : 0;
	if (SerOpen == 0) fatal("Can't open serial.device");

	/* set serial device parameters */
	MidiIn->io_Baud = 31250;
	/* DO NOT MAKE THIS BUFFER SMALLER OR YOU WILL BREAK GLIB FOR SOME SYNTHS!! */
        MidiIn->io_RBufLen = 10000;      /* large input buffer - if your synth
					 * sends dumps larger than this you
					 * will have to increase it. */
	MidiIn->io_SerFlags = SERF_RAD_BOOGIE;
	MidiIn->IOSer.io_Command = SDCMD_SETPARAMS;
	DoIO((struct IORequest *) MidiIn);

	/* clone MidiIn into MidiOut to allow simultaneous i/o */
        MidiOutPort = (struct MsgPort *) CreatePort("MidiOut",0L);
	if (MidiOutPort == NULL) fatal("Can't create MidiOutPort");

	MidiOut = (struct IOExtSer *)
			CreateExtIO(MidiOutPort, sizeof(struct IOExtSer));
	*MidiOut = *MidiIn;
	MidiOut->IOSer.io_Message.mn_ReplyPort = MidiOutPort;

	/* get the MIDI IN port started */
	startmidiread();
}

closemidi()
{
	if (SerOpen) CloseDevice((struct IORequest *)MidiIn);
	if (MidiIn) DeleteExtIO((struct IORequest *)MidiIn);
	if (MidiOut) DeleteExtIO((struct IORequest *)MidiOut);
	if (MidiInPort) DeletePort(MidiInPort);
	if (MidiOutPort) DeletePort(MidiOutPort);
}

flushmidi()
{
	while ( STATMIDI )
		getmidi();
}

#ifdef AZTEC_C
long milliclock()
{
    static long secs, startSecs, micros, startMicros;
    long product;

    CurrentTime(&secs, &micros);
    if (startSecs == 0) {
        startSecs = secs;
        startMicros = micros;
    }
    product =  ((secs - startSecs) * 1000L +
                (micros - startMicros)/1000L);
    return product;
}
#else
long milliclock()
{
	unsigned int clock[2];
	long milli;
	timer(clock);
	milli = clock[0] * 1000 + clock[1] / 1000;
	return milli;
}
#endif

millisleep(n)
{
    long ticks = n/20;

    if (ticks) Delay(ticks);        /* Avoid the Delay(0) bug! */
}

char *
alloc(n)
{
	char *p;

	if ( (p=malloc((unsigned)n)) == (char *)NULL ) {
		fatal("GLIB is out of memory!");
	}
	return(p);
}

struct TextAttr font =
{
	"topaz.font",TOPAZ_EIGHTY,FS_NORMAL,FPF_ROMFONT
};

struct NewScreen ns =
{
	0,0,640,200,2,BLACK,WHITE,HIRES,CUSTOMSCREEN,&font,"",NULL,NULL
};

windinit()
{
	struct NewWindow nw;

	if (S != NULL) return;

#ifdef ARP
	FileRequestImminent = FALSE;
	/* libraries opened by arpc.o */
#else
	IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 0);
	GfxBase = (struct GfxBase *) OpenLibrary("graphics.library",0);
	if (IntuitionBase == NULL || GfxBase == NULL) exit(1);
#endif

	if ((S = (struct Screen *) OpenScreen(&ns)) == NULL) exit(1);

	nw.LeftEdge = 0;
	nw.TopEdge = TOPEDGE;
	nw.Width = WindowWidth;
	nw.Height = WindowHeight;
	nw.DetailPen = BLACK;
	nw.BlockPen = WHITE;
	nw.Title = NULL;
	nw.Flags = SMART_REFRESH | ACTIVATE | BACKDROP |
		   BORDERLESS | NOCAREREFRESH | RMBTRAP;
	nw.IDCMPFlags = CLOSEWINDOW | VANILLAKEY;
	nw.Type = CUSTOMSCREEN;
	nw.FirstGadget = NULL;
	nw.CheckMark = NULL;
	nw.Screen = S;
	nw.BitMap = NULL;
	if ((W = (struct Window *) OpenWindow(&nw)) == NULL) exit(1);
	R = W->RPort;
	SetAPen(R, ORANGE);
	SetBPen(R, BLACK);
	ShowTitle(S, FALSE);
	Cols=80;
	Rows=24;
#ifdef ARP
	MyFileRequest.fr_Window = W;
#endif
}

windgoto(r,c)
int r,c;
{
	Move(R, c*8, r*8 + R->Font->tf_Baseline);
}

windeeol()
{
	int x, y;
	x = R->cp_x;
	y = R->cp_y  - R->Font->tf_Baseline;
	SetAPen(R, BLACK);
	RectFill(R, x, y, WindowWidth, y+R->Font->tf_YSize-1);
	SetAPen(R, ORANGE);
}

winderaserow(r)
{
	windgoto(r,0);
	windeeol();
}

windexit(r)
int r;
{
#ifdef ARP
	if (W) CloseWindowSafely(W,NULL);
#else
	if (W) CloseWindow(W);
#endif
	if (S) CloseScreen(S);
	exit(r);
}

windclear()
{
	SetAPen(R, BLACK);
	RectFill(R, 0, 0, WindowWidth, WindowHeight);
	SetAPen(R, ORANGE);
}

/* windgets - get a line of input from the console, handling backspaces */
windgets(s)
char *s;
{
	char *origs = s;
	int c;

#ifdef ARP
	if (FileRequestImminent) {
		FileRequestImminent = FALSE;
		if (FileRequest(&MyFileRequest)==NULL) {
      			message("No file name entered");
			strcpy(s,"");
      		}   
		else {
			strcpy(s,DirName);
			if(s[0]!=0 && (s[strlen(s)-1]!=':'))
				strcat(s,"/");
			strcat(s,FileName);
		}
	}
	else {
#endif
	SetAPen(R, WHITE);

	while ( (c=getconsole()) != '\n' && c!='\r' && c!= EOF ) {
		if ( c == '\b' ) {
			if ( s > origs ) {
				wbackspace();
				s--;
			}
		}
		else if (c == 24) {
			while (s > origs) {
				wbackspace();
				s--;
			}
		} else if (isprint(c)) {
			windputc(c);
			*s++ = c;
		}
		windrefresh();
	}
	*s = '\0';
	SetAPen(R, ORANGE);
#ifdef ARP
	}
#endif
}

windstr(s)
char *s;
{
#ifdef ARP
	if (!strnicmp(s,"File name",9)) {
		FileRequestImminent = TRUE;
		return;
	}
#endif

	/* The code below used to be just: Text(R, s, strlen(s)); */

	/* output is fastest if is many chars as possible can be */
	/* displayed in a single Text() call, so we scan for special */
	/* characters that need to be interpreted, and do the rest */
	/* in as few Text() calls as possible */
	register int i,a;
	for (i=0,a=0; ; ++i) {
		if (s[i] < ' ') {
			/* special character found */
			/* output everything up to this character */
			if (i-a>0) Text(R, s+a, i-a);
			/* interpret the special character */
			if (s[i] == 0) break;
			windputc(s[i]);
			/* continue after the special character */
			a = i+1;
		}
	}
}

windputc(c)
int c;
{
	/* The code below used to be just: char s = c; Text(R, &s, 1); */
	char s;
	switch (c) {
	case '\n':
		Move(R, 0, R->cp_y + 8);
		break;
	case '\r':
		Move(R, 0, R->cp_y);
		break;
	case '\t':
		Move(R, R->cp_x + 64 - (R->cp_x % 64), R->cp_y);
		break;
	case '\b':
		Move(R, R->cp_x - 8, R->cp_y);
		break;
	case '\007':
		beep();
		break;
	default:
		s = c;
		Text(R, &s, 1);
		break;
	}
}

wbackspace()
{
	int x, y;
	x = R->cp_x;
	y = R->cp_y;
	Move(R, x-8, y);
	windputc(' ');
	Move(R, x-8, y);
}

windrefresh()
{
}

beep()
{
	DisplayBeep(S);
}

windhigh()
{
}

windnorm()
{
}

struct IntuiText fataltext = { 1,0,COMPLEMENT,16,32,NULL,NULL,NULL };
struct IntuiText canceltext = { 1,0,COMPLEMENT,2,2,NULL,"cancel",NULL };

fatal(text)
char *text;
{
	fataltext.IText = text;
	AutoRequest(W, &fataltext, NULL, &canceltext, 0, 0, 320, 90);
	bye();
}

/****************
 * openls(), nextls(), and closels() are used to scan the current directory.
 ***************/

#ifdef AZTEC_C
#include "amigadir.h"
static DIR *dir;
openls()
{
    dir = opendir("");
}
char *
nextls()
{
    struct direct *result;

    if (dir) {
        if (result = readdir(dir)) {
            return result->d_name;
        }
        closels();
    }
    return NULL;
}

closels()
{
    if (dir) {
        closedir(dir);
        dir = NULL;
    }
}
#else
struct FileInfoBlock *lsinfo = NULL;

openls()
{
}

char *
nextls()
{
	int error;
	if (lsinfo == NULL) {
		lsinfo = (struct FileInfoBlock *) alloc(sizeof(struct FileInfoBlock));
		error = dfind(lsinfo, "#?", 0);
	} else {
		error = dnext(lsinfo);
	}
	if (error == 0) {
		return lsinfo->fib_FileName;
	} else {
		return NULL;
	}
}

closels()
{
	free(lsinfo);
	lsinfo = NULL;
}
#endif

