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

#include "glib.h"
#include <stdio.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;
}
