#include <proto/exec.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <stdio.h>

#include "SynthLib.h"
#include <libraries/locale.h>
#include <proto/locale.h>
#define CATCOMP_BLOCK
#define CATCOMP_NUMBERS
#include "ElectoneLoc.h"

#define BUFFER_SIZE         40000L

struct LocaleInfo DLi;
#ifndef DEBUG_SL
struct LocaleBase *LocaleBase;
struct DosBase *DOSBase;
struct ExecBase *SysBase;
#endif

char Message[256];
char ModelID=0x70;
unsigned char ElecHeader[]={0xF0,0x43,0x70,0x00,0x00,0xF7};
unsigned char AnswerElec[5];

#define STEPD_SEND_BLOCK 1
#define STEPD_SEND_END 2
#define STEPD_WAIT_SYSEX 3
#define STEPD_WAIT_YAMAHA 4
#define STEPD_CHECK_YAMAHA 5
#define STEPD_GET_BLOCK 6
#define STEPD_SEND_WAIT_ANSWER 7
#define STEPD_SEND_GET_ANSWER 8
static struct DriverDatas DD;
extern struct MinList ElectoneTypesList;
struct Node ElectoneTypesNodes[] = {
        &ElectoneTypesNodes[1], ( struct Node * )&ElectoneTypesList.mlh_Head, 0, 0, "Elec All",
        &ElectoneTypesNodes[2], &ElectoneTypesNodes[0], 0, 0, "Elec presets",
        &ElectoneTypesNodes[3], &ElectoneTypesNodes[1], 0, 0, "Elec CSP/RSP",
        &ElectoneTypesNodes[4], &ElectoneTypesNodes[2], 0, 0, "Elec FMP",
        &ElectoneTypesNodes[5], &ElectoneTypesNodes[3], 0, 0, "Elec rythms",
        &ElectoneTypesNodes[6], &ElectoneTypesNodes[4], 0, 0, "Elec arpegios",
        ( struct Node * )&ElectoneTypesList.mlh_Tail, &ElectoneTypesNodes[5], 0, 0, "Elec voices" };


struct MinList ElectoneTypesList = {
        ( struct MinNode * )&ElectoneTypesNodes[0], ( struct MinNode * )NULL, ( struct MinNode * )&ElectoneTypesNodes[6] };

struct DriverDatas * InitDriver(ULONG (*GetData)(ULONG Number));
void Close(void);
STRPTR GetStringD(struct LocaleInfo *li, LONG stringNum);
ULONG Driver(ULONG *StepPtr,struct Exchange *Ex);
STRPTR About(void);
UBYTE FindType(STRPTR TypeStr);

__saveds struct DriverDatas *InitDriver(ULONG (*GetData)(ULONG Number))
 {
#ifndef DEBUG_SL
  DOSBase=(struct DosBase *)GetData(GTD_DosBase);
  SysBase=(struct ExecBase *)GetData(GTD_ExecBase);
  LocaleBase=(struct LocaleBase *)GetData(GTD_LocaleBase);
#endif

  if (DLi.li_LocaleBase=LocaleBase)
    DLi.li_Catalog=OpenCatalog(NULL,"SynthLib/Electone.catalog",OC_BuiltInLanguage,"english",TAG_DONE);

  DD.Flags=0;
  DD.Driver=Driver;
  DD.Close=Close;
  DD.About=About;
  DD.TypeList=&ElectoneTypesList;

  return(&DD);
 }

__saveds void Close(void)
 {if (LocaleBase)
    CloseCatalog(DLi.li_Catalog);
 }

__saveds STRPTR About()
 {return(GetStringD(&DLi,MSGD_ABOUT));
 }

__saveds ULONG Driver(ULONG *StepPtr,struct Exchange *Ex)
 {static UBYTE * BufferElectone;
  static StepD;
  static UBYTE Type;
  static ULONG DatasLength;
  static struct DumpNode *Node;
  ULONG ReturnCode=0;

  switch(*StepPtr)
   {
    case STEP_INIT_DRIVER:

      BufferElectone=NULL;

      break;

    case STEP_EXIT_DRIVER:
    case STEP_ABORT:
      if (BufferElectone)
       {BufferElectone=0;
        FreeMem(BufferElectone,BUFFER_SIZE);
       }

    break;

    case STEP_START:
    case STEP_CONTINUE:

      *StepPtr=STEP_WAIT;

      break;

    case STEP_SEND_ABORT:

    break;

    case STEP_SEND_START:
      Node=Ex->Node;
      Type=FindType(Node->Type);

      if (Type!=255)
       {Ex->Message=GetStringD(&DLi,MSGD_SEND_TYPE0+Type);
        ElecHeader[3]=ModelID;
        ElecHeader[4]=Type+0x20;
        Ex->DatasPtr=ElecHeader;
        Ex->Length=6;
        *StepPtr=STEP_SEND_SEND;
        StepD=STEPD_SEND_BLOCK;
       }
       else
       {Ex->Message=GetStringD(&DLi,MSGD_UNKNOWN_TYPE);
        ReturnCode=1;
        *StepPtr=STEP_SEND_COMPLETE;
       }

      break;

    case STEP_SEND_CONTINUE:
      switch(StepD)
       {case STEPD_SEND_BLOCK:
          Ex->DatasPtr=Node->Dump;
          Ex->Length=Node->DumpLength;
          *StepPtr=STEP_SEND_SEND;
          StepD=STEPD_SEND_WAIT_ANSWER;

          break;

        case STEPD_SEND_WAIT_ANSWER:
          *StepPtr=STEP_SEND_WAIT;
          StepD=STEPD_SEND_GET_ANSWER;
          
          break;
          
        case STEPD_SEND_GET_ANSWER:
          Ex->DatasPtr=AnswerElec;
          Ex->Length=6;
          *StepPtr=STEP_SEND_RECIEVE_EOF;
          StepD=STEPD_SEND_END;
                    
        case STEPD_SEND_END:
          *StepPtr=STEP_SEND_COMPLETE;
          if (!memcmp(AnswerElec,"\0x43\0x70\0x70\0x38",4))
           {if (AnswerElec[4]==0x7F)
             {Ex->Message=Message;
              sprintf(Message,GetStringD(&DLi,MSGD_SEND_END),Node->Name);
             }
             else
             {Ex->Message=GetStringD(&DLi,MSGD_SEND_ERROR);
              ReturnCode=1;
             }
           }
           else
           {Ex->Message=Message;
            sprintf(Message,GetStringD(&DLi,MSGD_SEND_END),Node->Name);
           }
   
          break;
       }

      break;

    case STEP_REQ_START:
      Node=Ex->Node;

      Type=FindType(((struct Node *)Node)->ln_Name);

      if (Type!=255)
       {Ex->Message=GetStringD(&DLi,MSGD_REQUEST_TYPE0+Type);
        Ex->Length=6;
        ElecHeader[3]=ModelID;
        ElecHeader[4]=Type+0x10;
        Ex->DatasPtr=ElecHeader;

        *StepPtr=STEP_REQ_SEND;
        StepD=STEPD_WAIT_SYSEX;
       }
       else
       {Ex->Message=GetStringD(&DLi,MSGD_UNKNOWN_TYPE);
        ReturnCode=1;
        Ex->Node=NULL;
        *StepPtr=STEP_REQ_COMPLETE;
       }

      break;

    case STEP_REQ_ABORT:
      if (BufferElectone)
       {BufferElectone=0;
        FreeMem(BufferElectone,BUFFER_SIZE);
       }

      break;

    case STEP_REQ_CONTINUE:
      switch(StepD)
       {
        case STEPD_WAIT_SYSEX:
          if (!BufferElectone)
            BufferElectone=AllocMem(BUFFER_SIZE,0L);
          if (!BufferElectone)
           {Ex->Message=GetStringD(&DLi,MSGD_OUT_OF_MEMORY);
            Ex->Node=NULL;
            ReturnCode=1;
            *StepPtr=STEP_REQ_COMPLETE;
           }
           else
           {*StepPtr=STEP_REQ_WAIT;
            StepD=STEPD_WAIT_YAMAHA;
           }

          break;

        case STEPD_WAIT_YAMAHA:
          BufferElectone[0]=0xF0;
          Ex->DatasPtr=BufferElectone+1;
          Ex->Length=4;
          *StepPtr=STEP_REQ_RECIEVE;
          StepD=STEPD_CHECK_YAMAHA;

          break;

        case STEPD_CHECK_YAMAHA:
          if (Ex->DatasPtr[-4]==0x43 && Ex->DatasPtr[-3]==0x70 && !Ex->DatasPtr[-1])
           {StepD=STEPD_GET_BLOCK;
            Ex->Length=BUFFER_SIZE-5;
            Ex->Datas1=0xF7F7F7F7;
            *StepPtr=STEP_REQ_RECIEVE_EOF;
           }
           else
           {
            Ex->Node=NULL;
            *StepPtr=STEP_REQ_COMPLETE;
            Ex->Message=GetStringD(&DLi,MSGD_UNEXPECTED_ANSWER);
            ReturnCode=1;
           }

          break;

        case STEPD_GET_BLOCK:

          if (Ex->DatasPtr[-1]!=0xF7)
           {Ex->Message=GetStringD(&DLi,MSGD_TOO_MANY_DATAS);
            Ex->Node=NULL;
            ReturnCode=1;
            *StepPtr=STEP_REQ_COMPLETE;
           }
           else
           {DatasLength=Ex->DatasPtr-BufferElectone;
            if (Node=AllocMem(sizeof(struct DumpNode),0L))
             {Node->DNode.ln_Type=0;
              Node->DNode.ln_Name=Node->Name;
              Node->Datas=NULL;
              Node->DatasLength=0;
              Node->DumpLength=DatasLength;
              Node->Name[0]='\0';
              Node->Flags=0;
              Ex->Node=Node;
              strcpy(Node->Type,ElectoneTypesNodes[Type].ln_Name);

              if (Node->Dump=AllocMem(DatasLength,0L))
               {memcpy(Node->Dump,BufferElectone,DatasLength);

                Ex->DatasPtr=(APTR)Node;
                *StepPtr=STEP_REQ_COMPLETE;
                Ex->Message=GetStringD(&DLi,MSGD_RECIEVED_TYPE0+Type);
               }
               else
               {Ex->Message=GetStringD(&DLi,MSGD_OUT_OF_MEMORY);
                Ex->Node=NULL;
                ReturnCode=1;
                *StepPtr=STEP_REQ_COMPLETE;
                FreeMem(Node,sizeof(struct DumpNode));
               }
             }
             else
             {Ex->Message=GetStringD(&DLi,MSGD_OUT_OF_MEMORY);
              Ex->Node=NULL;
              ReturnCode=1;
              *StepPtr=STEP_REQ_COMPLETE;
             }
            FreeMem(BufferElectone,BUFFER_SIZE);
            BufferElectone=0;
           }

          break;
       }
      break;

   }
  return(ReturnCode);
}

UBYTE FindType(STRPTR TypeStr)
 {UBYTE Type=0;
  BOOL Flag=FALSE;

  while (!Flag && (Type<7))
    if (!strcmp(TypeStr,ElectoneTypesNodes[Type].ln_Name))
      Flag=TRUE;
     else
      Type++;

  if (!Flag)
    Type=255;

  return(Type);
 }

STRPTR GetStringD(struct LocaleInfo *li, LONG stringNum)
{
LONG   *l;
UWORD  *w;
STRPTR  builtIn;

    l = (LONG *)CatCompBlock;

    while (*l != stringNum)
    {
        w = (UWORD *)((ULONG)l + 4);
        l = (LONG *)((ULONG)l + (ULONG)*w + 6);
    }
    builtIn = (STRPTR)((ULONG)l + 6);

    if (LocaleBase)
        return(GetCatalogStr(li->li_Catalog,stringNum,builtIn));
    return(builtIn);
}
