//-----------------------------------------------------------------------------
// Danny Dulai <danny@ishiboo.com>  July 3, 2005
//
// Released in the public domain. You can do as you wish with this code.
//-----------------------------------------------------------------------------

#include <stdio.h>

#include "fmod.h"
#include "fmod_errors.h"
#include "All.h"
#include "MACLib.h"
#include "io.h"

FMOD_RESULT F_CALLBACK macopen(FMOD_CODEC *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo);
FMOD_RESULT F_CALLBACK macclose(FMOD_CODEC *codec);
FMOD_RESULT F_CALLBACK macread(FMOD_CODEC *codec, void *buffer, unsigned int size, unsigned int *read);
FMOD_RESULT F_CALLBACK macsetposition(FMOD_CODEC *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype);

FMOD_CODEC_DESCRIPTION rawcodec =
{
    "Monkey's Audio player plugin", // Name.
    0x00010000,                     // Version 0xAAAABBBB   A = major, B = minor.
    0,				    // defaultasstream
    FMOD_TIMEUNIT_PCM,              // The time format we would like to accept into setposition/getposition.
    &macopen,                       // Open callback.
    &macclose,                      // Close callback.
    &macread,                       // Read callback.
    0,                              // Getlength callback.  (If not specified FMOD return the length in FMOD_TIMEUNIT_PCM, FMOD_TIMEUNIT_MS or FMOD_TIMEUNIT_PCMBYTES units based on the lengthpcm member of the FMOD_CODEC structure).
    &macsetposition,                // Setposition callback.
    0,                              // Getposition callback. (only used for timeunit types that are not FMOD_TIMEUNIT_PCM, FMOD_TIMEUNIT_MS and FMOD_TIMEUNIT_PCMBYTES).
    0,                              // Sound create callback (don't need it)
};

class macio : public CIO {
    FMOD_CODEC *_codec;
    int _pos;

public:
    macio(FMOD_CODEC *codec) {
	_codec = codec;
	_pos = 0;
	pAPEDecompress = NULL;
	memset(&rawwaveformat, 0, sizeof(rawwaveformat));
    }

    ~macio() {
	delete pAPEDecompress;
    }

    IAPEDecompress *pAPEDecompress;
    FMOD_CODEC_WAVEFORMAT rawwaveformat;

    virtual int Read(void * pBuffer, unsigned int nBytesToRead, unsigned int * pBytesRead) {
	_codec->fileread(_codec->filehandle,
			 pBuffer, nBytesToRead, pBytesRead, NULL);
	_pos += *pBytesRead;
//	printf("CIO  Read %d/%d\n", nBytesToRead, *pBytesRead); fflush(stdout);
	return 0;
    }

    virtual int Seek(int nDistance, unsigned int nMoveMode) {
//	printf("CIO  Seek %d/%d\n", nDistance, nMoveMode); fflush(stdout);
	if (nMoveMode == 0) {
	    _pos = nDistance;
	} else if (nMoveMode == 1) {
	    _pos += nDistance;
	} else if (nMoveMode == 2) {
	    _pos = _codec->filesize + nDistance;
	}
	_codec->fileseek(_codec->filehandle, _pos, NULL);
	return 0;
    }
    virtual int GetSize() {
//	printf("CIO  GetSize %d\n", _codec->filesize); fflush(stdout);
	return _codec->filesize;
    }

    virtual int GetPosition() {
//	printf("CIO  GetPosition %d\n", _pos); fflush(stdout);
	return _pos;
    }

    virtual int Open(const wchar_t * pName) {
//	printf("CIO  Open\n"); fflush(stdout);
	return 0;
    }
    virtual int Open(const char * pName) {
//	printf("CIO  Open\n"); fflush(stdout);
	return 0;
    }
    virtual int Close() {
//	printf("CIO  Close\n"); fflush(stdout);
	return 0;
    }
    virtual int Create(const char * pName) {
//	printf("CIO  Create\n"); fflush(stdout);
	return 0;
    }
    virtual int Create(const wchar_t * pName) {
//	printf("CIO  Create\n"); fflush(stdout);
	return 0;
    }
    virtual int Delete() {
//	printf("CIO  Delete\n"); fflush(stdout);
	return 0;
    }
    virtual int SetEOF() {
//	printf("CIO  SetEOF\n"); fflush(stdout);
	return 0;
    }
    virtual int GetName(char * pBuffer) {
//	printf("CIO  GetName\n"); fflush(stdout);
	return 0;
    }
    virtual int GetName(wchar_t * pBuffer) {
//	printf("CIO  GetName\n"); fflush(stdout);
	return 0;
    }
    virtual int Write(const void * pBuffer, unsigned int nBytesToWrite, unsigned int * pBytesWritten) {
//	printf("CIO  Write\n"); fflush(stdout);
	return 0;
    }
};


#ifdef __cplusplus
extern "C" {
#endif

F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION * F_API FMODGetCodecDescription()
{
    return &rawcodec;
}

#ifdef __cplusplus
}
#endif


FMOD_RESULT F_CALLBACK
macopen(FMOD_CODEC *codec, FMOD_MODE usermode,
	FMOD_CREATESOUNDEXINFO *userexinfo)
{
//    printf("mac open\n"); fflush(stdout);
    int nRetVal=0;

    macio *m = new macio(codec);
    m->pAPEDecompress = CreateIAPEDecompressEx(m, &nRetVal);

    if (m->pAPEDecompress == NULL) {
//	printf("mac bad\n"); fflush(stdout);
	return FMOD_ERR_FILE_BAD;
    }

    int lenms = m->pAPEDecompress->GetInfo(APE_DECOMPRESS_LENGTH_MS);

    m->rawwaveformat.name[0]      = 0;
    m->rawwaveformat.channels     = m->pAPEDecompress->GetInfo(APE_INFO_CHANNELS);
    m->rawwaveformat.format       = FMOD_SOUND_FORMAT_PCM16;
    m->rawwaveformat.frequency    = m->pAPEDecompress->GetInfo(APE_INFO_SAMPLE_RATE);
    m->rawwaveformat.blockalign   = m->pAPEDecompress->GetInfo(APE_INFO_BLOCK_ALIGN);
    m->rawwaveformat.lengthpcm    =
	(int)((double)lenms / (double)1000.0 * (double)m->rawwaveformat.frequency);/* PCM samples */

//    printf("channels = %d\n", m->rawwaveformat.channels);
//    printf("freq = %d\n", m->rawwaveformat.frequency);
//    printf("blockalign = %d\n", m->rawwaveformat.blockalign);
//    printf("lenpcm = %d\n", m->rawwaveformat.lengthpcm);

    codec->waveformat   = &(m->rawwaveformat);
    codec->numsubsounds = 0;
    codec->userdata     = m;
//    printf("mac open done %d\n", lenms/1000); fflush(stdout);
    return FMOD_OK;
}

FMOD_RESULT F_CALLBACK
macclose(FMOD_CODEC *codec)
{
//    printf("mac close\n"); fflush(stdout);
    delete (macio*)codec->userdata;
//    printf("mac close done\n"); fflush(stdout);
    return FMOD_OK;
}

FMOD_RESULT F_CALLBACK
macread(FMOD_CODEC *codec, void *buffer, unsigned int size, unsigned int *read)
{
//    printf("mac read %d\n", size); fflush(stdout);
    macio *m = (macio*)codec->userdata;
    m->pAPEDecompress->GetData((char *)buffer,
			       size/codec->waveformat->blockalign, (int*)read);
    *read *= codec->waveformat->blockalign;
//    printf("mac read done %d\n", *read); fflush(stdout);
    return FMOD_OK;
}

FMOD_RESULT F_CALLBACK
macsetposition(FMOD_CODEC *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype)
{
//    printf("mac setpos %d %d %d\n", subsound, position, postype); fflush(stdout);
    macio *m = (macio*)codec->userdata;

    if (postype == FMOD_TIMEUNIT_PCM) {
//	printf("mac setpos seeking\n"); fflush(stdout);
	m->pAPEDecompress->Seek(position);
    }
//    printf("mac setpos done\n"); fflush(stdout);
    return FMOD_OK;
}

