#ifndef MAKE_ID
#define MAKE_ID(a,b,c,d) ((ULONG) (a)<<24 | (ULONG) (b)<<16 | (ULONG) (c)<<8 | (ULONG) (d))
#endif

//      NEW MIDI OUT TOOL
//      BY G.O.Jones 1998
//      -----------------

#include "bars.h"
#include <clib/debug_protos.h>
#include <clib/dos_protos.h>
#include <libraries/dos.h>
#include <devices/serial.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <exec/memory.h>
#include <string.h>
#include <intuition/intuition.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define ID_MOU MAKE_ID('M','O','U','T')
#define LEFT 7
#define TOP  15

void removetool(struct OutTool *tool);
void sendsysex(char *buff, short size);
int openserial(char *devicename, int unit);
void closeserial(void);
struct Event *processeventcode(struct Event *event);
static void deletetool(struct OutTool *copy);

extern struct Functions *functions;
struct Task *task;
BOOL serial = FALSE;
char name[300];
char un[300];
int unit=0;

static struct MsgPort  *SerialMP;         /* Message port pointer */
static struct IOExtSer *SerialIO;         /* I/O request pointer */

static UWORD chip outtool[]=
{
/*-------- plane # 0 --------*/

  0x0000,  0x0000,
  0x3fff,  0xff00,
  0x3000,  0x0300,
  0x3000,  0x0300,
  0x3000,  0x0300,
  0x3000,  0x0300,
  0x3100,  0x1300,
  0x3040,  0x8300,
  0x300c,  0x0300,
  0x3000,  0x0300,
  0x3fff,  0xff00,
  0x0000,  0x0000,

/*-------- plane # 1 --------*/

  0x0000,  0x0000,
  0x0000,  0x0300,
  0x0f8c,  0x7f00,
  0x0c0c,  0x0f00,
  0xc800,  0x0700,
  0xc000,  0x0300,
  0xc000,  0x0300,
  0xc800,  0x0700,
  0x0c00,  0x0f00,
  0x0f80,  0x7f00,
  0x1fff,  0xff00,
  0x0000,  0x0000,

/*-------- plane # 2 --------*/

  0x0000,  0x0000,
  0x0000,  0x0000,
  0x0073,  0x8000,
  0x03f3,  0xf000,
  0x07ff,  0xf800,
  0x0fff,  0xfc00,
  0x0eff,  0xec00,
  0x07bf,  0x7800,
  0x03f3,  0xf000,
  0x007f,  0x8000,
  0x0000,  0x0000,
  0x0000,  0x0000
};

 struct  Image outtoolimage =
 {
 0,0,
 24 , 12 , 3 ,
 outtool,
 0x7,0x00,
 NULL,
 };

struct OutTool {
                    struct Tool tool;
                    BOOL Note;
                    BOOL Pitch;
                    BOOL MonoAT;
                    BOOL PolyAT;
                    BOOL CChange;
                    BOOL PChange;
                    BOOL SysEx;
               };

extern struct Functions *functions;

// ----------------------------------------------

static struct IntuiText sysexgadText = {
        4,0,JAM1,
        2,1,
        NULL,
        "System Exclusive",
        NULL
};

static struct Gadget sysexgad = {
        NULL,
        LEFT,TOP+66,
        130,9,
        GADGHBOX+GADGHIMAGE,
        RELVERIFY+GADGIMMEDIATE+TOGGLESELECT,
        BOOLGADGET,
        NULL,
        NULL,
        &sysexgadText,
        NULL,
        NULL,
        6,
        NULL
};

static struct IntuiText pchangegadText = {
        4,0,JAM1,
        12,1,
        NULL,
        "Program Change",
        NULL
};

static struct Gadget pchangegad = {
        &sysexgad,
        LEFT,TOP+55,
        130,9,
        GADGHBOX+GADGHIMAGE,
        RELVERIFY+GADGIMMEDIATE+TOGGLESELECT,
        BOOLGADGET,
        NULL,
        NULL,
        &pchangegadText,
        NULL,
        NULL,
        5,
        NULL
};

static struct IntuiText cchangegadText = {
        4,0,JAM1,
        12,1,
        NULL,
        "Control Change",
        NULL
};

static struct Gadget cchangegad = {
        &pchangegad,
        LEFT,TOP+44,
        130,9,
        GADGHBOX+GADGHIMAGE,
        RELVERIFY+GADGIMMEDIATE+TOGGLESELECT,
        BOOLGADGET,
        NULL,
        NULL,
        &cchangegadText,
        NULL,
        NULL,
        4,
        NULL
};

static struct IntuiText polyATgadText = {
        4,0,JAM1,
        5,1,
        NULL,
        "Poly Aftertouch",
        NULL
};

static struct Gadget polyATgad = {
        &cchangegad,
        LEFT,TOP+33,
        130,9,
        GADGHBOX+GADGHIMAGE,
        RELVERIFY+GADGIMMEDIATE+TOGGLESELECT,
        BOOLGADGET,
        NULL,
        NULL,
        &polyATgadText,
        NULL,
        NULL,
        3,
        NULL
};

static struct IntuiText monoATgadText = {
        4,0,JAM1,
        5,1,
        NULL,
        "Mono Aftertouch",
        NULL
};

static struct Gadget monoATgad = {
        &polyATgad,
        LEFT,TOP+22,
        130,9,
        GADGHBOX+GADGHIMAGE,
        RELVERIFY+GADGIMMEDIATE+TOGGLESELECT,
        BOOLGADGET,
        NULL,
        NULL,
        &monoATgadText,
        NULL,
        NULL,
        2,
        NULL
};

static struct IntuiText pitchgadText = {
        4,0,JAM1,
        23,1,
        NULL,
        "Pitch Wheel",
        NULL
};

static struct Gadget pitchgad = {
        &monoATgad,
        LEFT,TOP+11,
        130,9,
        GADGHBOX+GADGHIMAGE,
        RELVERIFY+GADGIMMEDIATE+TOGGLESELECT,
        BOOLGADGET,
        NULL,
        NULL,
        &pitchgadText,
        NULL,
        NULL,
        1,
        NULL
};

static struct IntuiText notegadText = {
        4,0,JAM1,
        22,1,
        NULL,
        "Note On/Off",
        NULL
};

static struct Gadget notegad = {
        &pitchgad,
        LEFT,TOP,
        130,9,
        GADGHBOX+GADGHIMAGE,
        RELVERIFY+GADGIMMEDIATE+TOGGLESELECT,
        BOOLGADGET,
        NULL,
        NULL,
        &notegadText,
        NULL,
        NULL,
        0,
        NULL
};

// ------------------------------------------------

#define GadgetList  notegad;

static struct NewWindow outNewWindowStructure1 = {
        400,100,
        145,95,
        6,1,
        GADGETDOWN+CLOSEWINDOW,
        WINDOWDRAG+WINDOWDEPTH+WINDOWCLOSE+ACTIVATE+NOCAREREFRESH,
        &notegad,
        NULL,
        "New MIDI Out 1",
        NULL,
        NULL,
        5,5,
        0,0,
        CUSTOMSCREEN
};

void setgadgettext(struct Window *window, short id, char *text)
{
    struct Gadget *gadget = (struct Gadget *) (*functions->GetGadget)(window,id);
    if (gadget) {
        gadget->GadgetText->IText = text;
        (functions->DrawEmbossed)(window,id);
    }
}

void sendsysex(char *buff,short size)
{
            if(size>1500)
            (*functions->transportcommand)(TC_STOP,functions->timenow);

            SerialIO->IOSer.io_Length=size;
            SerialIO->IOSer.io_Data=(APTR)buff;
            SerialIO->IOSer.io_Command=CMD_WRITE;
            if(serial)
            {
                DoIO((struct IORequest *)SerialIO);
                WaitTOF();
            }
}

void edittoolcode(struct OutTool *tool)
{
    struct IntuiMessage *message;
    struct Window *window;
    long class, code;
    short refresh = 1;
    struct Gadget *gadget;
    struct NewWindow *newwindow;

    outNewWindowStructure1.Screen = functions->screen;
    if (tool->tool.touched & TOUCH_EDIT) {
        outNewWindowStructure1.LeftEdge = tool->tool.left;
        outNewWindowStructure1.TopEdge = tool->tool.top;
    }
    if (!tool->tool.touched) {
        tool->tool.touched = TOUCH_INIT;

                    tool->Note= TRUE;
                    tool->Pitch= TRUE;
                    tool->MonoAT= TRUE;
                    tool->PolyAT= TRUE;
                    tool->CChange= TRUE;
                    tool->PChange= TRUE;
                    tool->SysEx= FALSE;
    }
    newwindow = (struct NewWindow *)
        (*functions->DupeNewWindow)(&outNewWindowStructure1);
    if (!newwindow) return;
    newwindow->Title = 0;
    newwindow->Flags |= BORDERLESS;
    newwindow->Flags &= ~0xF;
    newwindow->DetailPen = 0;
    newwindow->BlockPen = 0;
    window = (struct Window *)
        (*functions->FlashyOpenWindow)(newwindow);
    if (!window) return;
    tool->tool.window = window;
    (*functions->EmbossWindowOn)(window,WINDOWCLOSE|WINDOWDEPTH|WINDOWDRAG,
        "New Midi Out",(short)-1,(short)-1,0,0);

    (*functions->EmbossOn)(window,0,1);
    (*functions->EmbossOn)(window,1,1);
    (*functions->EmbossOn)(window,2,1);
    (*functions->EmbossOn)(window,3,1);
    (*functions->EmbossOn)(window,4,1);
    (*functions->EmbossOn)(window,5,1);
    (*functions->EmbossOn)(window,6,1);


    for (;;) {
        if (refresh)
        {

            (*functions->SelectEmbossed)(window,0,tool->Note);
            (*functions->SelectEmbossed)(window,1,tool->Pitch);
            (*functions->SelectEmbossed)(window,2,tool->MonoAT);
            (*functions->SelectEmbossed)(window,3,tool->PolyAT);
            (*functions->SelectEmbossed)(window,4,tool->CChange);
            (*functions->SelectEmbossed)(window,5,tool->PChange);
            (*functions->SelectEmbossed)(window,6,tool->SysEx);

        }
        refresh = 0;
        message = (struct IntuiMessage *) (*functions->GetIntuiMessage)(window);
        class = message->Class;
        code = message->Code;
        gadget = (struct Gadget *) message->IAddress;
        class = (*functions->SystemGadgets)(window,class,gadget,code);
        ReplyMsg((struct Message *)message);
        if (class == CLOSEWINDOW) break;
        else if (class == GADGETDOWN) {


            }


        else if (class == GADGETUP)  {

            switch(gadget->GadgetID)
            {
                      case 0:
                      tool->Note = !tool->Note;
                      refresh = 1;
                      break;

                      case 1:
                      tool->Pitch = !tool->Pitch;
                      refresh = 1;
                      break;

                      case 2:
                      tool->MonoAT = !tool->MonoAT;
                      refresh = 1;
                      break;

                      case 3:
                      tool->PolyAT = !tool->PolyAT;
                      refresh = 1;
                      break;

                      case 4:
                      tool->CChange = !tool->CChange;
                      refresh = 1;
                      break;

                      case 5:
                      tool->PChange = !tool->PChange;
                      refresh = 1;
                      break;

                      case 6:
                      tool->SysEx = !tool->SysEx;
                      refresh = 1;
                      break;

                      default:
                        break;

            }
       }
    }

    (*functions->EmbossOff)(window,0);
    (*functions->EmbossOff)(window,1);
    (*functions->EmbossOff)(window,2);
    (*functions->EmbossOff)(window,3);
    (*functions->EmbossOff)(window,4);
    (*functions->EmbossOff)(window,5);
    (*functions->EmbossOff)(window,6);

    tool->tool.window = 0;
    tool->tool.left = window->LeftEdge;
    tool->tool.top = window->TopEdge;
    tool->tool.touched = TOUCH_INIT | TOUCH_EDIT;
    (*functions->FlashyCloseWindow)(window);
    (*functions->DeleteNewWindow)(newwindow);
}

extern printf();

struct Event *processeventcode(struct Event *event)
{
    UBYTE data[10];
    UBYTE channel;
    struct OutTool *tool = (struct OutTool *) event->tool;
    BOOL skip = FALSE;

    if(!serial)
    {
        if((openserial(name, unit)) != 3) serial = FALSE;
        else serial = TRUE;
    }

    channel = (UBYTE)tool->tool.track->channelout;

    if (!tool->tool.touched) {
        tool->tool.touched = TOUCH_INIT;
                    tool->Note= TRUE;
                    tool->Pitch= TRUE;
                    tool->MonoAT= TRUE;
                    tool->PolyAT= TRUE;
                    tool->CChange= TRUE;
                    tool->PChange= TRUE;
                    tool->SysEx= FALSE;
    }                                    

    switch (event->status)
    {
        case MIDI_NOTEON:
        if(tool->Note)
        {
            data[0] = 0x90|channel;
            data[1] = event->byte1;
            data[2] = event->byte2;
            SerialIO->IOSer.io_Length=3;
            SerialIO->IOSer.io_Data=(APTR)data;
            SerialIO->IOSer.io_Command=CMD_WRITE;
        }
        break;

        case MIDI_NOTEOFF:
        if(tool->Note)
        {
            data[0] = 0x80|channel;
            data[1] = event->byte1;
            data[2] = event->byte2;
            SerialIO->IOSer.io_Length=3;
            SerialIO->IOSer.io_Data=(APTR)data;
            SerialIO->IOSer.io_Command=CMD_WRITE;
        }
        break;

        case MIDI_PTOUCH:
        if(tool->PolyAT)
        {
            data[0] = 0xA0|channel;
            data[1] = event->byte1;
            data[2] = event->byte2;
            SerialIO->IOSer.io_Length=3;
            SerialIO->IOSer.io_Data=(APTR)data;
            SerialIO->IOSer.io_Command=CMD_WRITE;
        }
        break;

        case MIDI_MTOUCH:
        if(tool->MonoAT)
        {
            data[0] = 0xD0|channel;
            data[1] = event->byte1;
            SerialIO->IOSer.io_Length=2;
            SerialIO->IOSer.io_Data=(APTR)data;
            SerialIO->IOSer.io_Command=CMD_WRITE;
        }
        break;

        case MIDI_PCHANGE:
        if(tool->PChange)
        {
            data[0] = 0xC0|channel;
            data[1] = event->byte1;
            SerialIO->IOSer.io_Length=2;
            SerialIO->IOSer.io_Data=(APTR)data;
            SerialIO->IOSer.io_Command=CMD_WRITE;
        }
        break;

        case MIDI_PBEND:
        if(tool->Pitch)
        {
            data[0] = 0xE0|channel;
            data[1] = event->byte1;
            data[2] = event->byte2;
            SerialIO->IOSer.io_Length=3;
            SerialIO->IOSer.io_Data=(APTR)data;
            SerialIO->IOSer.io_Command=CMD_WRITE;
        }
        break;

        case MIDI_CCHANGE:
            if(tool->CChange)
            {
                data[0] = 0xB0|channel;
                data[1] = (UBYTE)event->byte1;
                data[2] = (UBYTE)event->byte2;
                SerialIO->IOSer.io_Length=3;
                SerialIO->IOSer.io_Data=(APTR)data;
                SerialIO->IOSer.io_Command=CMD_WRITE;
            }
        break;

        case MIDI_SYSX:
        if(tool->SysEx)
        {
            struct StringEvent *se= (struct StringEvent *) event;

            sendsysex(se->string->string, se->string->length);
        }
        break;

        case MIDI_MTC:
        data[0] = 0xF1;
        SerialIO->IOSer.io_Length=1;
        SerialIO->IOSer.io_Data=(APTR)data;
        SerialIO->IOSer.io_Command=CMD_WRITE;
        break;

        case MIDI_CLOCK:
        data[0] = 0xF8;
        SerialIO->IOSer.io_Length=1;
        SerialIO->IOSer.io_Data=(APTR)data;
        SerialIO->IOSer.io_Command=CMD_WRITE;
        break;

        case MIDI_SONGPP:
        data[0] = 0xF2;
        data[1] = event->byte1;
        data[2] = event->byte2;
        SerialIO->IOSer.io_Length=3;
        SerialIO->IOSer.io_Data=(APTR)data;
        SerialIO->IOSer.io_Command=CMD_WRITE;
        break;

        case MIDI_SONGS:
        data[0] = 0xF3;
        SerialIO->IOSer.io_Length=1;
        SerialIO->IOSer.io_Data=(APTR)data;
        SerialIO->IOSer.io_Command=CMD_WRITE;
        break;

        case MIDI_START:
        data[0] = 0xFA;
        SerialIO->IOSer.io_Length=1;
        SerialIO->IOSer.io_Data=(APTR)data;
        SerialIO->IOSer.io_Command=CMD_WRITE;
        break;

        case MIDI_STOP:
        data[0] = 0xFC;
        SerialIO->IOSer.io_Length=1;
        SerialIO->IOSer.io_Data=(APTR)data;
        SerialIO->IOSer.io_Command=CMD_WRITE;
        break;

        case MIDI_CONTINUE:
        data[0] = 0xFB;
        SerialIO->IOSer.io_Length=1;
        SerialIO->IOSer.io_Data=(APTR)data;
        SerialIO->IOSer.io_Command=CMD_WRITE;
        break;

        case MIDI_SENSE:
        data[0] = 0xFE;
        SerialIO->IOSer.io_Length=1;
        SerialIO->IOSer.io_Data=(APTR)data;
        SerialIO->IOSer.io_Command=CMD_WRITE;
        break;

        default:
        break;
    }

        if(serial)DoIO((struct IORequest *)SerialIO);

(*functions->freeevent)(event);
return(0);
}

static struct ToolMaster master;


void   removetool(struct OutTool *tool)
{
    closeserial();
}

struct ToolMaster *inittoolmaster(void)
{
    BPTR prefs;
    int i;

    memset((char *)&master,0,sizeof(struct ToolMaster));
    master.toolsize = sizeof(struct OutTool);
    master.toolid = ID_MOU;
    master.edittool = edittoolcode;
    master.deletetool = deletetool;
    master.removetool = removetool;
    master.image = &outtoolimage;
    strcpy(master.name,"New MIDI Out 1");
    master.processevent = processeventcode;
    master.tooltype = TOOL_OUTPUT|TOOL_ONTIME|TOOL_MIDI;

    prefs = Open("envarc:NewMidiOut1.prefs",MODE_OLDFILE);

    if(prefs)
    {
        FGets(prefs,name,299);
        for(i=0; i<299; i++)
        {
            if(isspace(name[i])) break;
        }

        name[i] = NULL;

        FGets(prefs,un,299);
        for(i=0; i<299; i++)
        {
            if(isspace(un[i])) break;
        }

        un[i] = NULL;
        unit = atoi(un);
        Close(prefs);
    }
    return(&master);
}

int openserial(char *devicename, int unit)
{
        if (SerialMP=CreateMsgPort())
        {
            if (SerialIO=(struct IOExtSer *)CreateExtIO(SerialMP,sizeof(struct IOExtSer)) )
            {
                SerialIO->io_SerFlags|= SERF_SHARED;

                if (OpenDevice(devicename,unit,(struct IORequest *)SerialIO,0))
                {
                    return 0;
                }
                else
                {
                    SerialIO->IOSer.io_Command  = SDCMD_QUERY;
                    DoIO((struct IORequest *)SerialIO);             /* execute query */

                    SerialIO->IOSer.io_Command  = SDCMD_SETPARAMS;
                    SerialIO->io_SerFlags &= ~SERF_PARTY_ON;
                    SerialIO->io_SerFlags|=(SERF_RAD_BOOGIE|SERF_XDISABLED);
                    SerialIO->io_Baud = 31250;
                    DoIO((struct IORequest *)SerialIO);

                }
            }
            else return 1;
        }
        else return 2;

return 3;
}

void closeserial(void)
{
    if (SerialIO)
    {
        if (!(CheckIO(SerialIO)))
        {
            AbortIO((struct IORequest *)SerialIO); 
        }                                     
        WaitIO((struct IORequest *)SerialIO);

        CloseDevice((struct IORequest *)SerialIO);
        DeleteExtIO((struct IORequest *)SerialIO);
        DeletePort(SerialMP);
    }
}


static void deletetool(struct OutTool *copy)
{
  functions->myfree(copy, sizeof(struct OutTool));
}

