/*
 * GLIB - a Generic LIBrarian and editor for synths
 *
 * Machine dependent stuff.
 *
 * Amiga version.
 */

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

#include <dos.h>
#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>

#include <proto/exec.h>
#include <proto/intuition.h>

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
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;

/* getmouse - get current row and column of mouse */
getmouse(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;

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.
 */

getmidi()
{
	register struct Message	*io;
	int result;

	/* return previously-received data, if available */
	if (MidiDataAvail) {
		result = MidiInBuf;
		/* 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 a byte to MIDI OUT
 */

sendmidi(c)
int c;
{
	char	buf = c;

	MidiOut->IOSer.io_Data = (APTR) &buf;
	MidiOut->IOSer.io_Length = 1;
	MidiOut->IOSer.io_Command = CMD_WRITE;
	DoIO((struct IORequest *) MidiOut);	/* synchronous request */
}

/*
 * 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,0);
	if (MidiInPort == NULL) fatal("Can't create MidiInPort");

	/* create i/o request block for serial device */
	MidiIn = (struct IOExtSer *)
		CreateExtIO(MidiInPort, 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;
	MidiIn->io_RBufLen = 8192;	/* 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",0);
	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();
}

long milliclock()
{
	unsigned int clock[2];
	long milli;
	timer(clock);
	milli = clock[0] * 1000 + clock[1] / 1000;
	return milli;
}

millisleep(n)
{
	Delay(n/20);
}

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;

	IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 0);
	GfxBase = (struct GfxBase *) OpenLibrary("graphics.library",0);
	if (IntuitionBase == NULL || GfxBase == NULL) exit(1);

	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;
}

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;
{
	if (W) CloseWindow(W);
	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;

	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);
}

windstr(s)
char *s;
{
	Text(R, s, strlen(s));
}

windputc(c)
int c;
{
	char s = c;
	Text(R, &s, 1);
}

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.
 ***************/

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;
}
