#ifndef NOSOUND
#include <AL/al.h>
#include <AL/alc.h>
#include <AL/alut.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>


#include "globals.h"


#ifndef NOSOUND
typedef struct {
  ALsizei size;
  ALsizei bits;
  ALsizei freq;
  ALsizei format;
  void *wave;
} wavSound;


void setvolume();
void stopsoundinloop(int soundnum);
void playwave(int soundnum, ALfloat *Position);
int playwaveinloop(int soundnum);
void LoadWavs();
ALboolean SourceIsPlaying(ALuint sid);


ALuint sounddesc[MAX_SOUNDS];
ALuint soundbuf[MAX_SOUNDS];
wavSound Wavs[NUM_SOUNDS];
ALuint loopsounddesc[MAX_LOOPING_SOUNDS];
ALuint loopsoundbuf[MAX_LOOPING_SOUNDS];
#endif

int volume;
int newvolume;
int WarpSoundNum;
int ThrustSoundNum;
int ShieldSoundNum;
float volumevals[MAX_VOLUME];


void initsound(int *argc, char *argv[])
{
#ifndef NOSOUND
  int i, j;

  alutInit(argc, argv);

  for (i=0; i<MAX_SOUNDS; i++) {
    sounddesc[i]=0;
    soundbuf[i]=0;
  }

  for (i=0; i<MAX_LOOPING_SOUNDS; i++) {
    loopsounddesc[i]=0;
    loopsoundbuf[i]=0;
  }

  for (i=0; i<=MAX_VOLUME; i++) {
    volumevals[i]=1.0;
    for (j=MAX_VOLUME; j>i; j--) {
      volumevals[i]/=1.6;
    }
  }

  newvolume=volume;
  setvolume();

  ThrustSoundNum=-1;
  ShieldSoundNum=-1;
  WarpSoundNum=-1;

  LoadWavs();
#endif
}


void setvolume()
{
  if (volume > MAX_VOLUME) {
    volume=MAX_VOLUME;
  }

  if (volume < 0) {
    volume=0;
  }

#ifndef NOSOUND
  alListenerf(AL_GAIN, volumevals[volume]);
#endif
}


// Function to play a sound file
void playsound(int soundnum, struct Point *Position=&ZeroPosition)
{
#ifndef NOSOUND
  ALfloat SoundPosition[3];
  float Distance;

  SoundPosition[0]=Position->x;
  SoundPosition[1]=Position->y;
  SoundPosition[2]=Position->z;
  if (soundnum==NukeExplodeSound) {
    SoundPosition[0]/=2.5;
    SoundPosition[1]/=2.5;
    SoundPosition[2]/=2.5;
  } else if (soundnum==NukeAlertSound) {
    if (SoundPosition[0]*SoundPosition[0]+
        SoundPosition[1]*SoundPosition[1]+
        SoundPosition[2]*SoundPosition[2] > 25.0) {
      return;
    }
    SoundPosition[0]=0.0;
    SoundPosition[1]=0.0;
    SoundPosition[2]=0.0;
  }

  playwave(soundnum, SoundPosition);
#endif
}


// Function to play a sound file continuously
int playsoundinloop(int soundnum)
{
#ifndef NOSOUND
  return(playwaveinloop(soundnum));
#endif

  return(-1);
}


void stopsoundinloop(int soundnum)
{
#ifndef NOSOUND
  alSourceStop(loopsounddesc[soundnum]);
#endif
}


#ifndef NOSOUND
void playwave(int soundnum, ALfloat *Position)
{
  ALsizei size;
  ALsizei bits;
  ALsizei freq;
  ALsizei format;
  ALboolean err;
  void *wave;
  int i;

  if (!volume) {
    return;
  }

  // Find an open sound buffer to add sound to, and clean up existing buffers
  for (i=0; i<MAX_SOUNDS; i++) {
    if (SourceIsPlaying(sounddesc[i]) == AL_TRUE) {
      if (i==MAX_SOUNDS) {
        return;
      }
    } else {
      if (sounddesc) {
        alDeleteBuffers(1, &soundbuf[i]);
        alDeleteSources(1, &sounddesc[i]);
        soundbuf[i]=0;
        sounddesc[i]=0;
      }
      alGenBuffers(1, &soundbuf[i]);
      break;
    }
  }

  alBufferData(soundbuf[i], Wavs[soundnum].format,
               Wavs[soundnum].wave, Wavs[soundnum].size,
               Wavs[soundnum].freq);

  alGenSources(1, &sounddesc[i]);
  alSourcefv(sounddesc[i], AL_POSITION, Position);
  alSourcei(sounddesc[i], AL_BUFFER, soundbuf[i]);
  alSourcei(sounddesc[i], AL_LOOPING, AL_FALSE);

  alSourcePlay(sounddesc[i]);
}


int playwaveinloop(int soundnum)
{
  ALsizei size;
  ALsizei bits;
  ALsizei freq;
  ALsizei format;
  ALboolean err;
  void *wave;
  int i;

  if (!volume) {
    return(-1);
  }

  // Find an open sound buffer to add sound to, and clean up existing buffers
  for (i=0; i<MAX_LOOPING_SOUNDS; i++) {
    if (SourceIsPlaying(loopsounddesc[i]) == AL_TRUE) {
      if (i==MAX_LOOPING_SOUNDS) {
        return(-1);
      }
    } else {
      if (loopsounddesc) {
        alDeleteBuffers(1, &loopsoundbuf[i]);
        alDeleteSources(1, &loopsounddesc[i]);
        loopsoundbuf[i]=0;
        loopsounddesc[i]=0;
      }
      alGenBuffers(1, &loopsoundbuf[i]);
      break;
    }
  }

  alBufferData(loopsoundbuf[i], Wavs[soundnum].format,
               Wavs[soundnum].wave, Wavs[soundnum].size,
               Wavs[soundnum].freq);

  alGenSources(1, &loopsounddesc[i]);
  alSourcei(loopsounddesc[i], AL_BUFFER, loopsoundbuf[i]);
  alSourcei(loopsounddesc[i], AL_LOOPING, AL_TRUE);

  alSourcePlay(loopsounddesc[i]);

  return(i);
}


// Function to check if a sound is playing in the buffer sid
ALboolean SourceIsPlaying(ALuint sid)
{
  ALint state;

  if (alIsSource(sid) == AL_FALSE) {
    return(AL_FALSE);
  }

  state = AL_INITIAL;
  alGetSourceiv(sid, AL_SOURCE_STATE, &state);

  switch(state) {
  case AL_PLAYING:
  case AL_PAUSED:
    return(AL_TRUE);
  default:
    break;
  }

  return(AL_FALSE);
}
#endif


void CleanupSounds()
{
#ifndef NOSOUND
  if (ShieldSoundNum!=-1) {
    stopsoundinloop(ShieldSoundNum);
    ShieldSoundNum=-1;
  }
  if (ThrustSoundNum!=-1) {
    stopsoundinloop(ThrustSoundNum);
    ThrustSoundNum=-1;
  }
  if (WarpSoundNum!=-1) {
    stopsoundinloop(WarpSoundNum);
    WarpSoundNum=-1;
  }
#endif
}


#ifndef NOSOUND
void LoadWavs()
{
  int i;
  char filename[1000];

  for (i=0; i<NUM_SOUNDS; i++) {
#ifdef WINDOWS
    sprintf(filename, "sounds/%d.wav", i);
#else
    sprintf(filename, "%s/atr3d/sounds/%d.wav", DATADIR, i);
#endif

    alutLoadWAV(filename, &Wavs[i].wave, &Wavs[i].format,
                &Wavs[i].size, &Wavs[i].bits, &Wavs[i].freq);
  }
}
#endif
