/*
*    ___                       ____                  __
*   / _ \___ _    _____ ____  / __/__  __ _____  ___/ /
*  / ___/ _ \ |/|/ / -_) __/ _\ \/ _ \/ // / _ \/ _  /
* /_/   \___/__,__/\__/_/   /___/\___/\_,_/_//_/\_,_/
*
*
*   Original Code by Max Watson
*   Written November 1, 1999
*   To be released under the LGPL
*
*   PowerSound sample manipulation routines
*   Mostly taken from the SDL-demo mixer library
*   borrowed (with changes) by Max Watson <max@blackholesun.com>
*
*   See LICENSE.TXT for more LGPL info
*/

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include "SDL/SDL_audio.h"
#include "SDL/SDL_mutex.h"
#include "SDL/SDL_timer.h"

#include "powersound.h"

extern SDL_mutex *mixer_lock;
extern PS_Channel channel[PS_MIX_CHANNELS];

void PS_SamplePanSet(PS_Sample *sample, int pan)
{
    if(sample!=NULL)
        sample->pan = pan;
}
int  PS_SamplePanGet(PS_Sample *sample)
{
    if(sample!=NULL)
        return sample->pan;
    return(-1);
}

void PS_SampleDepthSet(PS_Sample *sample, int depth)
{
    if(sample!=NULL)
        sample->depth = depth;
}
int PS_SampleDepthGet(PS_Sample *sample)
{
    if(sample!=NULL)
        return sample->depth;
    return(-1);
}

PS_Sample *PS_SampleLoad(const char *file)
{
    PS_Sample *chunk;
    SDL_AudioSpec wavespec;
    SDL_AudioCVT wavecvt;
    SDL_RWops *src;
    int audio_opened;
    SDL_AudioSpec mixer;

    audio_opened = PS_AudioQuery(&mixer.freq, &mixer.format, &mixer.channels);
    src = SDL_RWFromFile(file, "rb");
    /* Make sure audio has been opened */
    if ( ! audio_opened ) {
        SDL_SetError("Audio device hasn't been opened");
        SDL_RWclose(src);
        return(NULL);
    }

    /* Allocate the chunk memory */
    chunk = (PS_Sample *)malloc(sizeof(PS_Sample));
    if ( chunk == NULL ) {
        SDL_SetError("Out of memory");
        SDL_RWclose(src);
        return(NULL);
    }

    /* Load the WAV file into the chunk */
    if ( SDL_LoadWAV_RW(src, 1,
                        &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen) == NULL ) {
        free(chunk);
        return(NULL);
    }

    /* Build the audio converter and create conversion buffers */
    if ( SDL_BuildAudioCVT(&wavecvt,
                           wavespec.format, wavespec.channels, wavespec.freq,
                           mixer.format, mixer.channels, mixer.freq) < 0 ) {
        SDL_FreeWAV(chunk->abuf);
        free(chunk);
        return(NULL);
    }
    wavecvt.len = chunk->alen;
    wavecvt.buf = (Uint8 *)malloc(wavecvt.len*wavecvt.len_mult);
    if ( wavecvt.buf == NULL ) {
        SDL_SetError("Out of memory");
        SDL_FreeWAV(chunk->abuf);
        free(chunk);
        return(NULL);
    }
    memcpy(wavecvt.buf, chunk->abuf, chunk->alen);
    SDL_FreeWAV(chunk->abuf);

    /* Run the audio converter */
    if ( SDL_ConvertAudio(&wavecvt) < 0 ) {
        free(wavecvt.buf);
        free(chunk);
        return(NULL);
    }
    chunk->abuf = wavecvt.buf;
    chunk->alen = wavecvt.len_cvt;
    chunk->volume = PS_MAX_VOLUME;
    return(chunk);
}

void PS_SampleFree(PS_Sample *sample)
{
    int i;

    /* Caution -- if the sample is playing, the mixer will crash */
    if ( sample ) {
        /* Guarantee that this sample isn't playing */
        SDL_mutexP(mixer_lock);
        for ( i=0; i<PS_MIX_CHANNELS; ++i ) {
            if ( sample == channel[i].chunk ) {
                channel[i].playing = 0;
            }
        }
        SDL_mutexV(mixer_lock);

        /* Actually free the sample */
        free(sample->abuf);
        free(sample);
    }
}

void PS_SampleVolume(PS_Sample *sample,  int volume)
{
    if(volume>=0 && volume<=128)
        if(sample!=NULL)
            sample->volume = volume;
    return;
}

int PS_SamplePlayOnChannel(int which, PS_Sample *sample, int loops)
{
    int i;

    /* Don't play null pointers :-) */
    if ( sample == NULL ) {
        return(-1);
    }

    /* Lock the mixer while modifying the playing channels */
    SDL_mutexP(mixer_lock);
    {
        /* If which is -1, play on the first free channel */
        if ( which == -1 ) {
            for ( i=0; i<PS_MIX_CHANNELS; ++i ) {
                if ( channel[i].playing <= 0 )
                    break;
            }
            if ( i == PS_MIX_CHANNELS ) {
                which = -1;
            } else {
                which = i;
            }
        }

        /* Queue up the audio data for this channel */
        if ( which >= 0 ) {
            channel[which].samples = sample->abuf;
            channel[which].playing = sample->alen;
            channel[which].looping = loops;
            channel[which].chunk = sample;
        }
    }
    SDL_mutexV(mixer_lock);

    /* Return the channel on which the sound is being played */
    return(which);
}

extern int  PS_SamplePlay3DOnChannel(int channel, PS_Sample *sample,
                                     int pan, int depth, int loops)
{
    int _pan, _depth, _chan;

    if(sample == NULL)
        return(-1);

    // Temporarily set the samples pan and depth
    _pan = sample->pan;
    _depth = sample->depth;
    sample->pan = pan;
    sample->depth = depth;

    //Play the sample
    _chan =  PS_SamplePlayOnChannel(channel, sample, loops);

    // Restore the original pan and depth
    sample->pan = _pan;
    sample->depth = _depth;

    /* Return the channel on which the sound is being played */
    return _chan;
}

