#include <stdlib.h>
#include <stdio.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <devices/audio.h>
#include <clib/alib_protos.h>
#include <clib/exec_protos.h>
#include <proto/exec.h>
#include <proto/dos.h>

#include "snd.h"         /* Definitions for the sound stuff */

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

#define VHDR    MakeID('V', 'H', 'D', 'R')
#define BODY    MakeID('B', 'O', 'D', 'Y')

#define clock   3546895

UBYTE                           *bufdata[] = {NULL, NULL, NULL};
Voice8Header    vhdr[3];
ULONG                           size[] = {0L, 0L, 0L};
struct IOAudio  *playeriob[] = {NULL, NULL, NULL, NULL};

enum {ER_NONE, ER_OPENFILE, ER_READFILE, ER_IOB, ER_ALLOCMEM};

struct IOAudio *GetAudioChannel(UBYTE *);
void FreeAudioChannel(struct IOAudio *);

int setupsound(void) {
        BPTR            file;
        UBYTE           buffer[8];
        ULONG           filebuffer_len;
        UBYTE           *buf, *bufstart;
        BPTR            lock;
        char            t[40];
        char            u[55];
        int             itt, itter;
        UBYTE           map[1];
        int             error = ER_NONE;

        lock = GetProgramDir();
        NameFromLock(lock, t, 40);
        
        sprintf(u, "%s/sounds.8svx", t);
        
        if (file = (BPTR)Open(u, MODE_OLDFILE)) {
                if (Read(file, (APTR)buffer, 8L) == 8) {
                        
                        filebuffer_len = ((struct Chunk *)buffer)->ckSize;
                        bufstart = buf = (UBYTE *)AllocMem(filebuffer_len, MEMF_PUBLIC | MEMF_CLEAR);

                        if (Read(file, (APTR)buf, filebuffer_len) == filebuffer_len) {

                                for (itt = 0; itt < 3; itt++) {
                                        while(((struct Chunk *)buf)->ckID != VHDR)             buf++;
                                        buf += 8L;
                                        vhdr[itt].samplesPerSec = ((Voice8Header *)(buf))->samplesPerSec;
                                        while (((struct Chunk *)buf)->ckID != BODY)    buf++;
                                        size[itt] = ((struct Chunk *)buf)->ckSize;
                                        buf += 8L;
                                        
                                        bufdata[itt] = (UBYTE *)AllocMem(size[itt], MEMF_CHIP | MEMF_CLEAR);
                                        if (!bufdata[itt]) {
                                                FreeMem((APTR)bufstart, filebuffer_len);
                                                Close(file);
                                                return ER_ALLOCMEM;
                                        }
                                        
                                        for (itter = 0;itter<size[itt];itter++)
                                                *((bufdata[itt])+itter) = *(buf++);
                                }
                        }
                        else error = ER_READFILE;
                }
                else error = ER_READFILE;
        }
        else error = ER_OPENFILE;
        
        if (file)               Close(file);
        if (bufstart)   FreeMem((APTR)bufstart, filebuffer_len);

        if (error) return error;

        /*******************************************
         *  Set up the player AudioRequests
         *******************************************/

        for (itt = 0; itt < 4; itt++) {
                map[0] = 1 << itt;
                playeriob[itt] = GetAudioChannel(map);
                if (!playeriob[itt]) return ER_IOB;
        }
        
        return ER_NONE;

}

enum {EXPLODE, START, SHOOT};

void explode(int player) {
        
/*******************************************
 *  Do explosionIO
 *******************************************/
        
        struct IOAudio *iob;
        
        iob = playeriob[player - 1];

        if (iob) {
                iob->ioa_Request.io_Command = ADCMD_FINISH;
                iob->ioa_Request.io_Flags =     IOF_QUICK;
                BeginIO((struct IORequest *)iob);

                iob->ioa_Request.io_Command =   CMD_WRITE;
                iob->ioa_Request.io_Flags =     ADIOF_PERVOL;
                iob->ioa_Data =                                         bufdata[EXPLODE];
                iob->ioa_Length =                               size[EXPLODE];
                iob->ioa_Period =                               clock / vhdr[EXPLODE].samplesPerSec;
                iob->ioa_Volume =                               64;
                iob->ioa_Cycles =                               1;
                BeginIO((struct IORequest *)iob);
        }
}

void start(int player) {
        
/*******************************************
 *  Do startIO
 *******************************************/

        struct IOAudio *iob;
        
        iob = playeriob[player - 1];
        
        if (iob) {
                iob->ioa_Request.io_Command = ADCMD_FINISH;
                iob->ioa_Request.io_Flags =     IOF_QUICK;
                BeginIO((struct IORequest *)iob);

                iob->ioa_Request.io_Command = CMD_WRITE;
                iob->ioa_Request.io_Flags =     ADIOF_PERVOL;
                iob->ioa_Data =                                         bufdata[START];
                iob->ioa_Length =                               size[START];
                iob->ioa_Period =                               clock / vhdr[START].samplesPerSec;
                iob->ioa_Volume =                               64;
                iob->ioa_Cycles =                               1;
                BeginIO((struct IORequest *)iob);
        }
}

void shoot(int player) {
        
/*******************************************
 *  Do shootIO
 *******************************************/

        struct IOAudio *iob;
        
        iob = playeriob[player - 1];

        if (iob) {
                iob->ioa_Request.io_Command = ADCMD_FINISH;
                iob->ioa_Request.io_Flags =     IOF_QUICK;
                BeginIO((struct IORequest *)iob);

                iob->ioa_Request.io_Command = CMD_WRITE;
                iob->ioa_Request.io_Flags =     ADIOF_PERVOL;
                iob->ioa_Data =                                 bufdata[SHOOT];
                iob->ioa_Length =                               size[SHOOT];
                iob->ioa_Period =                               clock / vhdr[SHOOT].samplesPerSec;
                iob->ioa_Volume =                               32;
                iob->ioa_Cycles =                               1;
                BeginIO((struct IORequest *)iob);
        }
}

void closesound(void) {
        int itt;
        
        for (itt = 0; itt < 4; itt++)
                FreeAudioChannel(playeriob[itt]);
        
        for (itt = 0; itt < 3; itt++)
                if (bufdata[itt]) FreeMem(bufdata[itt], size[itt]);
}

/* Get an audio channel */
struct IOAudio *GetAudioChannel(UBYTE *allocationMap) {
        struct IOAudio *aIOB;
        struct MsgPort *aPort;
        long err;

        aPort=CreateMsgPort();
        if(!aPort)      return(NULL);

        aIOB=(struct IOAudio *)AllocMem(sizeof(struct IOAudio),
                                                                                                MEMF_PUBLIC | MEMF_CLEAR);
        if(!aIOB) {
                DeletePort(aPort);
                return(NULL);
        }

        /* Set up the IOAudio to allocate the command channel */
        aIOB->ioa_Request.io_Message.mn_Node.ln_Pri =   0;
        aIOB->ioa_Request.io_Message.mn_ReplyPort =             aPort;
        aIOB->ioa_Length =                                                                              1;
        aIOB->ioa_Data =                                                                                        allocationMap;
        aIOB->ioa_Request.io_Command =                                          ADCMD_ALLOCATE;

        /*Open the audio device*/
        err = OpenDevice(AUDIONAME, 0, (struct IORequest *)aIOB, 0);

        if(!aIOB->ioa_AllocKey) {  /*There was an error*/
                DeletePort(aPort);
                FreeMem(aIOB, sizeof(struct IOAudio));
                return(NULL);
        }
        else {
                /* Set up the IOAudio for writes */
                aIOB->ioa_Request.io_Command =  CMD_WRITE;
                aIOB->ioa_Request.io_Flags =            ADIOF_PERVOL;
                aIOB->ioa_Length =                                      0;
                return(aIOB);
        }
}

/* Free an allocated audio channel */
void FreeAudioChannel(struct IOAudio *aIOB) {
        if(aIOB == NULL)
                return;

        /* Free the audio channel */
        aIOB->ioa_Request.io_Command=ADCMD_FREE;
        BeginIO((struct IORequest *)aIOB);
        WaitIO((struct IORequest *)aIOB);
        DeletePort(aIOB->ioa_Request.io_Message.mn_ReplyPort);

        /* Close the audio channel */
        CloseDevice((struct IORequest *)aIOB);

        /* Free the IOAudio structure */
        FreeMem(aIOB,sizeof(struct IOAudio));
        return;
}

