/* This file is part of OpenBubbles.
 *
 * OpenBubbles is an SDL clone of Bubbles.
 * Copyright (C) 2004  Benny Sperisen
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * audioplayer.cpp is the implementation file of audioplayer.h.
 *
 * Written by:
 *  Benny Sperisen
 *  lasindi@gmail.com
 *  www.freewebs.com/lasindi
 *****************************************************************************/

#include "audioplayer.h"

audioPlayer *audio;

audioPlayer::audioPlayer()
{
  SDL_AudioSpec wanted;
  wanted.freq=44100;
  wanted.format=AUDIO_S16;
  wanted.samples=4096;
  wanted.channels=2;
  wanted.callback=callback;
  wanted.userdata=NULL;
  if(SDL_OpenAudio(&wanted,&spec)<0)
  {
    cerr<<"Error: SDL_OpenAudio\n"<<SDL_GetError()<<endl;
    exit(1);
  }
  numSounds=-1;
}

audioPlayer::~audioPlayer()
{
  for(hash_map<int,sound_c>::iterator it=sounds.begin();it!=sounds.end();it++)
    (*it).second.freeSamples();
}

int audioPlayer::loadSound(string filename)
{
  sound_c sound;
  Uint8 *s_samples;
  Uint32 s_length;
  SDL_AudioCVT cvt;
  SDL_AudioSpec loaded;
  Uint8* newbuffer;
  if(SDL_LoadWAV(filename.c_str(),&loaded,&s_samples,&s_length)==
     NULL)
  {
    cerr<<"Error: SDL_LoadWAV\n"<<SDL_GetError()<<endl;
    return -1;
  }
  sound.setSamples(s_samples);
  sound.setLength(s_length);
  if(SDL_BuildAudioCVT(&cvt,loaded.format,loaded.channels,loaded.freq,
                       spec.format,spec.channels,spec.freq)<0)
  {
    cerr<<"Error: SDL_BuildAudioCVT\n"<<SDL_GetError()<<endl;
    SDL_FreeWAV(sound.getSamples());
    return -1;
  }
  cvt.len=sound.getLength();
  newbuffer=(Uint8 *)malloc(sound.getLength()*cvt.len_mult);
  memcpy(newbuffer,s_samples,sound.getLength());
  cvt.buf=newbuffer;
  if(SDL_ConvertAudio(&cvt)<0)
  {
    cerr<<"Error: SDL_ConvertAudio\n"<<SDL_GetError()<<endl;
    SDL_FreeWAV(sound.getSamples());
    return -1;
  }
  SDL_FreeWAV(sound.getSamples());
  sound.setSamples(newbuffer);
  sound.setLength(sound.getLength()*cvt.len_mult);
  numSounds++;
  sounds[numSounds]=sound; // Put sound into sounds with numSounds as its key.
  return numSounds;
}

void audioPlayer::clearSounds()
{
  playing.clear();
}

void audioPlayer::playSound(int key)
{
  playing_c play(sounds[key]);
  SDL_LockAudio();
  playing.push_back(play);
  SDL_UnlockAudio();
}

void audioPlayer::callback(void *user_data,Uint8 *audio,int length)
{
  Uint8 *soundbuffer;
  Uint32 soundlength;
  memset(audio,0,length);
  for(list<playing_c>::iterator it=playing.begin();
      it!=playing.end();it++)
  {
    soundbuffer=(*it).getSound().getSamples();
    soundbuffer+=(*it).getPosition();
    if((*it).getPosition()+length>(*it).getSound().getLength())
      soundlength=(*it).getSound().getLength()-(*it).getPosition();
    else
      soundlength=length;
		if(!_muted)
			SDL_MixAudio(audio,soundbuffer,soundlength,VOLUME_PER_SOUND);
    (*it).setPosition((*it).getPosition()+length);
    if((*it).getPosition()>=(*it).getSound().getLength())
    {
      playing.erase(it);
      it--;
    }
  }
}

bool audioPlayer::isMuted() const
{
	return _muted;
}

void audioPlayer::mute(bool on)
{
	_muted=on;
}

audioPlayer::sound_c::sound_c(Uint8 *samples,Uint32 length)
  :_samples(samples),_length(length)
{}

void audioPlayer::sound_c::freeSamples()
{
  free(_samples);
}

Uint8* audioPlayer::sound_c::getSamples() const
{
  return _samples;
}

Uint32 audioPlayer::sound_c::getLength() const
{
  return _length;
}

void audioPlayer::sound_c::setSamples(Uint8 *samples)
{
  _samples=samples;
}

void audioPlayer::sound_c::setLength(Uint32 length)
{
  _length=length;
}

audioPlayer::playing_c::playing_c(sound_c sound,Uint32 position)
  :_sound(sound),_position(position)
{}

audioPlayer::sound_c audioPlayer::playing_c::getSound() const
{
  return _sound;
}

Uint32 audioPlayer::playing_c::getPosition() const
{
  return _position;
}

void audioPlayer::playing_c::setPosition(Uint32 position)
{
  _position=position;
}

// static variables
list<audioPlayer::playing_c> audioPlayer::playing;
SDL_AudioSpec audioPlayer::spec;
hash_map<int,audioPlayer::sound_c> audioPlayer::sounds;
int audioPlayer::numSounds=-1;
bool audioPlayer::_muted;
