#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <exec/devices.h>
#include <devices/timer.h>
#include <devices/audio.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <stdio.h>
#include "randcmd.h"

extern struct ExecBase      *SysBase;
extern struct DosLibrary    *DOSBase;
struct FileInfoBlock        *fib;
struct Lock                 *flock;

struct MsgPort              *CreatePort();
struct MsgPort              *AudioPort;
struct IOAudio               Audio;
struct MsgPort              *Timermessage;
struct timerequest           TimerReq;
struct MsgPort              *CommandPort;
struct commandrequest       *cmdmessage;
struct Message              *GetMsg();

char *FilePointer;
UBYTE Channels[] = { 0x0F };
short AudioDev = NULL,TimerDev = NULL;
UBYTE *sounddata;
ULONG  samlen;


char RandSamVers[50] = {"Randsam v1.1 , by Steven Lagerweij 030490"};

#define ERR_NOTFOUND  -10
#define ERR_ILLEGAL   -11
#define ERR_NOSAMPLES -12

struct SampleData sample[50];

LONG current = 0;
LONG avail   = 0;

struct Values var;

LONG rand(void);
LONG skipiff;
char ConfigName[50] = {"s:play.config"};

#define mindelay var.vl_mindelay
#define maxdelay var.vl_maxdelay
#define mincyc   var.vl_mincyc
#define maxcyc   var.vl_maxcyc
#define maxdiff  var.vl_maxdiff
#define diff     var.vl_diff
#define speed    var.vl_speed
#define Cyc      var.vl_Cyc
#define vol      var.vl_vol
#define waittime var.vl_waittime

FILE *cfp;

long secs;

VOID main()
{
ULONG sig;
LONG cmd,ret,quit = FALSE;
    if(FindPort(CMDPORTNAME))
      { printf("Randsam is already running!\n"); exit(0); }

    OpenAll();
    if((ret = ReadConfig()) != NULL)
      {
      switch(ret)
        {
        case ERR_NOTFOUND  : printf("Config file not found!\n"); break;
        case ERR_ILLEGAL   : printf("Minimum and maximum delay values are incorrect\n"); break;
        case ERR_NOSAMPLES : printf("Must have at least one sample\n"); break;
        }
      Quit();
      }
    if(mindelay < 1) mindelay = 1;
    while(!quit)
      {
        secs = (rand() % (maxdelay - mindelay)) + mindelay;
        TimerReq.tr_time.tv_secs = secs;
        TimerReq.tr_time.tv_micro= 0;
        SendIO(&TimerReq.tr_node);
waitstate:
        var.vl_randseed = SysBase->DispCount;
        srand(var.vl_randseed); /* Get a (I hope) random seed */
        if(quit) Quit();
        sig = Wait(1<<Timermessage->mp_SigBit | 1<<CommandPort->mp_SigBit);

        if(sig & (1<<CommandPort->mp_SigBit))
          {
            cmdmessage = (struct commandrequest *)GetMsg(CommandPort);
            if(cmdmessage != NULL)
              {
                cmd = cmdmessage->Command;
                switch(cmd)
                  {
                    case COM_QUIT : quit = TRUE; break;
                    case COM_PLAY : for(current=0;current<avail;current++)
                                      {
                                         Cyc = 1; vol = 64;
                                         speed = sample[current].Period;
                                         PlaySample();
                                      }
                                    break;
                    case COM_CONF :
                                    waittime = TimerReq.tr_time.tv_secs;
                                    cmdmessage->data1 = (char *)&sample[current];
                                    cmdmessage->data2 = (char *)&var;
                                    break;
                  }
                ReplyMsg(cmdmessage);
              }
          }
        if(sig & (1<<Timermessage->mp_SigBit))
          {
            GetMsg(Timermessage);
            var.vl_cur = current = (rand() % avail);
            diff = 0; vol = 64; Cyc = 1;
            if(maxdiff)
              {
                diff = ((WORD)rand() % (WORD)maxdiff);
                if(rand() % 2 == 0) diff *= -1;
              }
            if((Cyc = sample[current].Cycles) == 0)
              {
                if(maxcyc > 0) Cyc = (WORD)(rand() % (maxcyc-(mincyc-1)))+mincyc;
                else Cyc = 1;
              }
            vol = (WORD)sample[current].Vol;
            if((vol < 1) || (vol > 64)) vol = (WORD)(rand() % 64)+1;
            if((speed = sample[current].Period + diff) < 1) speed = 124;
            PlaySample();
          }
        else goto waitstate; /* prevent multiple timer requests */
      }
Quit();
exit(0);
}

ReadConfig()
{
ULONG temp;

    if(!(cfp = fopen(&ConfigName[0],"r"))) return(ERR_NOTFOUND);

    fscanf(cfp,"%ld",&temp); var.vl_mindelay = temp;
    fscanf(cfp,"%ld",&temp); var.vl_maxdelay = temp;
    fscanf(cfp,"%ld",&temp); var.vl_maxdiff  = temp;
    fscanf(cfp,"%ld",&temp); var.vl_mincyc   = temp;
    fscanf(cfp,"%ld",&temp); var.vl_maxcyc   = temp;
    fscanf(cfp,"%ld",&temp); avail = var.vl_num = temp;
    if(maxcyc > 0) if(mincyc < 1) { mincyc = 1; maxcyc++; }

    if(mindelay > maxdelay)
      { temp = mindelay; mindelay = maxdelay; maxdelay = temp; }
    else if((maxdelay == 0) || (maxdelay == mindelay)) return(ERR_ILLEGAL);
    if(avail < 1) return(ERR_NOSAMPLES);
    if(avail > 49) avail = 49;
    for(current = 0;current < avail;current++)
      {
        fscanf(cfp,"%s",&(sample[current].Name[0]));
        fscanf(cfp,"%ld",&temp); (sample[current].Period) = temp;
        fscanf(cfp,"%ld",&temp); (sample[current].Vol) = temp;
        fscanf(cfp,"%ld",&temp); (sample[current].Cycles) = temp;
      }
    fclose(cfp);
    return(0);
}

LoadFile()
{
  int ret = FALSE;
  fib = NULL; FilePointer = NULL; flock = NULL; sounddata = NULL;

  if(!(fib = (struct FileInfoBlock *)
      AllocMem((ULONG)sizeof(struct FileInfoBlock),(ULONG)MEMF_CLEAR)))
       goto quit;
  if(!(FilePointer = (char *)Open(sample[current].Name,1005))) goto quit;
  if(!(flock = (struct Lock *)Lock(sample[current].Name,SHARED_LOCK))) goto quit;
  if(!(Examine(flock,fib))) goto quit;
  samlen = fib->fib_Size;
  sounddata = (UBYTE *)AllocMem((LONG)samlen,MEMF_CHIP | MEMF_CLEAR);
  if(sounddata == NULL) goto quit;

  Read(FilePointer,sounddata,samlen);
  skipiff = CountIFF();
  ret = TRUE;
quit:
  if(FilePointer) Close(FilePointer);
  if(flock)       UnLock(flock);
  if(fib)         FreeMem(fib, (ULONG) sizeof(struct FileInfoBlock));
  return(ret);
}


PlaySample()
{
int ret = FALSE, t = 0;
    AudioPort = 0;
    AudioDev  = TRUE;
    sounddata = NULL;

    if(!LoadFile()) goto qt2;
    if(!(AudioPort = CreatePort("randsam Audioport"))) goto quit;
    Audio.ioa_Request.io_Message.mn_Node.ln_Pri = 50;
    Audio.ioa_Request.io_Message.mn_ReplyPort   = AudioPort;
    Audio.ioa_Request.io_Command                = ADCMD_ALLOCATE;
    Audio.ioa_Data                              = Channels;
    Audio.ioa_Length                            = (long)sizeof(Channels);

    AudioDev = OpenDevice(AUDIONAME,0,&Audio,0);
    if(AudioDev != NULL) goto quit;

again:
    Audio.ioa_Request.io_Command = CMD_WRITE;
    Audio.ioa_Request.io_Flags   = ADIOF_PERVOL | ADIOF_SYNCCYCLE;
    Audio.ioa_Request.io_Unit    = (struct Unit *)(rand() % 4);
    Audio.ioa_Data               = (UBYTE *)(sounddata+skipiff);
    Audio.ioa_Length             = samlen;
    Audio.ioa_Period             = speed;
    Audio.ioa_Cycles             = Cyc;
    Audio.ioa_Volume             = vol;

    BeginIO(&Audio);
    if(CheckIO(&Audio))
      {
        t++; WaitIO(&Audio);
        if(t<25) goto again;
      }
    WaitIO(&Audio);
    ret = TRUE;
quit:
    if(!AudioDev)   CloseDevice(&Audio);
    if(AudioPort)   DeletePort(AudioPort);
qt2:
    if(sounddata)   FreeMem(sounddata,samlen);
return(ret);
}

OpenAll()
{
 if(!(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",0)))
    { printf("No dos.library!\n"); Quit(); };

 if((CommandPort=CreatePort(CMDPORTNAME, 0))==NULL)
    { printf("Cannot setup command port\n"); Quit(); };
 if((Timermessage=CreatePort("Randsam Timer Port", 0))==NULL)
    { printf("Cannot setup a timer port\n"); Quit(); };
 TimerReq.tr_node.io_Message.mn_ReplyPort=Timermessage;
 TimerReq.tr_node.io_Command=TR_ADDREQUEST;
 TimerReq.tr_node.io_Flags=0;
 TimerReq.tr_node.io_Error=0;
 if((TimerDev = OpenDevice(TIMERNAME,UNIT_VBLANK,&TimerReq,0)) != NULL)
    { printf("Cannot open timer.device!\n"); Quit(); };

return(0);
}


Quit()
{
    if(DOSBase) CloseLibrary(DOSBase);
    AbortIO(&TimerReq.tr_node);
    if(!TimerDev)    CloseDevice(&TimerReq);
    if(Timermessage) DeletePort(Timermessage);
    if(CommandPort)  DeletePort(CommandPort);
exit(0); return(0);
}

CountIFF()
{
    long cnt = 4,ret = 0;

    if( (sounddata[0] == 'F') &&
        (sounddata[1] == 'O') &&
        (sounddata[2] == 'R') &&
        (sounddata[3] == 'M'))
      {
        while(cnt < (samlen-4))
          {
            if( (sounddata[cnt+0] == 'B') &&
                (sounddata[cnt+1] == 'O') &&
                (sounddata[cnt+2] == 'D') &&
                (sounddata[cnt+3] == 'Y'))
                    { ret = cnt+8; cnt = samlen; }
            cnt++;
          }
      }
return(ret);
}
