/*
 * Sound.c
 * By: Odis
 */
#include <windows.h>
#include "main.h"

#include <string.h>

#ifdef DX
/* the sound table! */
const struct sounds sound_table[MAX_SOUND+1] =
{
  /* fname                        name                          bytes */
 {"audio/song1.wav",            "Song1",                        1368058},
 
 {"audio/whoa-strange.wav", "Whoa-Strange",                      34672},


 /* Make this last, always! */
 {"Not Applicable!",       "None",             0  }
};

/* Get song via name */
int get_sound( char *name )
{
  int i = 0;
  
  for ( i = 0; i < MAX_SOUND; i++ )
   if ( !stricmp(sound_table[i].name, name) )
    return i;

  /* return "None" */
  return MAX_SOUND+1;
}

/* get sound via buf */
SOUND *get_sound_buf( LPDIRECTSOUNDBUFFER buf )
{
   SOUND *s = NULL;
   
   for ( s = first_sound; s != NULL; s = s->next )
    if ( buf == s->sound )
     return s;
   
   return NULL;
}

/* find a sound by number, load it, play it */
void play_sound( int s )
{
   if ( s < 0 || s > MAX_SOUND )
    return;

   LPDIRECTSOUNDBUFFER b = load_wave_from_file( sound_table[s].fname, sound_table[s].bytes, s );

   b->lpVtbl->SetCurrentPosition(b,60);

   /* play but don't loop */
   play_wave( b, FALSE );
}

/* globals...*/
HMMIO wave;
int volume;
bool stopped;
int current_song;

SOUND *first_sound = NULL;
SOUND *last_sound = NULL;

LPDIRECTSOUND ds = NULL;
LPDIRECTSOUNDBUFFER song = NULL;
/***/

/* close DX stuff */
void close_dx( void )
{
  SOUND *s = NULL;
  SOUND *snext = NULL;
  
  glog( "In close_dx..." );
  
  /* unalloc all standing buffers */
  for ( s = first_sound; s != NULL; s = snext )
  {
     snext = s->next;
     delete_wave(s->sound);
  }

  ds->lpVtbl->Release(ds);
  mmioClose( wave, 0 ); /* close handle to open wave */
}

/* unalloc a buffer */
void delete_wave( LPDIRECTSOUNDBUFFER buf )
{
   if ( buf == NULL )
    return;
    
   SOUND *s = get_sound_buf(buf);

   if ( song == buf )
     song = NULL;

   if ( buf != NULL )
     buf->lpVtbl->Release(buf);

   if ( s != NULL )
   {
    glog( "Deleting wave... (Index: %d)", s->index );

    s->sound = NULL; /* set pointer to null */
    
    UNLINK(s, first_sound, last_sound, next, prev );
    DISPOSE(s);
   }
}

/* intialize the buffer that the sound will reside in */
LPDIRECTSOUNDBUFFER init_buffer( DWORD bytes, int index )
{
   SOUND *s = NULL;
   HRESULT hr;
   WAVEFORMATEX wfx;
   ZeroMemory( &wfx, sizeof(WAVEFORMATEX));
   
   wfx.wFormatTag = (WORD) WAVE_FORMAT_PCM;
   wfx.nChannels  = 1;
   wfx.nSamplesPerSec = 22050;
   wfx.wBitsPerSample = 8;
   wfx.nBlockAlign = (WORD) (wfx.wBitsPerSample / 8 * wfx.nChannels);
   wfx.nAvgBytesPerSec = (DWORD) (wfx.nSamplesPerSec * wfx.nBlockAlign);
   
   DSBUFFERDESC dsbd;
   ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
   
   dsbd.dwSize = sizeof(DSBUFFERDESC);
   dsbd.dwFlags = DSBCAPS_CTRLVOLUME|DSBCAPS_GETCURRENTPOSITION2;
   
glog( "Init_buffer: bytes = %lu", bytes );

   dsbd.dwBufferBytes = URANGE(DSBSIZE_MIN,bytes,DSBSIZE_MAX);
/*       dsbd.guid3DAlgorithm = GUID_NULL;*/
   dsbd.lpwfxFormat = &wfx;
   
   LPDIRECTSOUNDBUFFER buffer = NULL;

   hr = ds->lpVtbl->CreateSoundBuffer(ds, &dsbd, &buffer, NULL );
   
   buffer->lpVtbl->SetFormat(buffer, &wfx);
   
   if FAILED(hr)
    return NULL;

   /* we keep track of sounds via list */
   if ( buffer != NULL )
   {
      CREATE( s, SOUND, 1);
      s->sound = buffer;
      s->index = index;
      
      LINK( s, first_sound, last_sound, next, prev );
   }
    
   return buffer;
}

/* load an entire wave file */
LPDIRECTSOUNDBUFFER load_wave_from_file( LPSTR file, DWORD bytes, int index )
{
   if ( !file || file[0] == '\0' )
     return NULL;

   HMMIO wve = NULL;
   
   wve = mmioOpen( file, NULL, MMIO_READ );
   
   if ( wve == NULL )
   {
    mmioClose( wve, 0 );
    return NULL;
   }
   
   VOID* locked = NULL; /* pointer to locked buffer memory */
   DWORD lsize  = 0;    /* size of the locked buffer */
   LPDIRECTSOUNDBUFFER buf = init_buffer(bytes, index);
   
   if ( buf == NULL )
   {
    mmioClose( wve, 0);
    return NULL;
   }
   
   HRESULT hr;
   hr = buf->lpVtbl->Lock(buf, 0, bytes, &locked, &lsize, NULL, NULL, DSBLOCK_ENTIREBUFFER);
   
   if FAILED(hr)
   {
    delete_wave(buf);
    mmioClose( wve, 0 );
    return NULL;
   }
    
   /* start from the beginning */
   mmioSeek( wve, 0, SEEK_SET);
   
   int rt;
   rt = mmioRead( wve, (HPSTR)locked, (LONG)bytes );
   
   if ( rt == 0 )
    lsize = bytes;
   else if ( rt == -1 )
   {
    delete_wave(buf);
    mmioClose( wve, 0 );
    return NULL;
   }
   
   buf->lpVtbl->Unlock(buf, locked, lsize, NULL, 0 );
   
   /* unalloc this, we don't need it anymore */
   mmioClose( wve, 0 );
   
   return buf;
}

/* play the buffer */
void play_wave( LPDIRECTSOUNDBUFFER buf, bool loop )
{
  if ( buf == NULL )
   return;

  if ( loop == TRUE )
    buf->lpVtbl->Play(buf,0,0,DSBPLAY_LOOPING);
  else
    buf->lpVtbl->Play(buf,0,0,0);
    
  stopped = FALSE;
}

/* stop the said audio */
void stop_wave( LPDIRECTSOUNDBUFFER buf )
{
   if ( buf == NULL )
    return;
    
   buf->lpVtbl->Stop(buf);
   
   stopped = TRUE;
}

/* set the volume */
void set_volume( LPDIRECTSOUNDBUFFER buf, LONG amount )
{
  if ( buf == NULL )
    return;
  
  volume = URANGE(DSBVOLUME_MIN,amount,DSBVOLUME_MAX);
    
  buf->lpVtbl->SetVolume(buf,volume);
}

/* initialize directsound stuff */
bool init_dx( int song )
{
  bool ok = FALSE;

  current_song = song;

  if ( DirectSoundCreate( 0, &ds, 0 ) == DS_OK )
   if ( ds->lpVtbl->SetCooperativeLevel(ds,game->hWindow,DSSCL_PRIORITY) == DS_OK )
     ok = TRUE;
    
  if ( ok == TRUE )
  {
  /*song = load_wave_from_file( (LPSTR)SONG_FILE, sound_table[curreng_song].bytes );*/

    stopped = FALSE;
    volume = -200;

    /* the song for streaming */
    if ( current_song != MAX_SOUND )
    {
       wave = mmioOpen( sound_table[current_song].fname, NULL, MMIO_READ ); /* open */
       handle_dx(TRUE); /* get the song some data */
    }
    return TRUE;

   /*song = load_wave_from_resource( songmem, sound_table[current_song].bytes );*/
  }


  return FALSE;
}

/* load a wave file from resource - the whole thing*/
LPDIRECTSOUNDBUFFER load_wave_from_resource( HGLOBAL mem, DWORD bytes, int index )
{
   if ( mem == NULL )
     return NULL;

   VOID* locked = NULL; /* pointer to locked buffer memory */
   DWORD lsize  = 0;    /* size of the locked buffer */
   LPDIRECTSOUNDBUFFER buf = init_buffer(bytes, index);

   if ( buf == NULL )
    return NULL;

   HRESULT hr;
   hr = buf->lpVtbl->Lock(buf, 0, bytes, &locked, &lsize, NULL, NULL, DSBLOCK_ENTIREBUFFER);

   if FAILED(hr)
   {
    delete_wave(buf);
    return NULL;
   }

   BYTE *ptr;
   ptr = mem;
   
   memcpy(locked,&ptr[44],lsize);

   buf->lpVtbl->Unlock(buf, locked, lsize, NULL, 0 );

   return buf;
}

/* update anything that needs it, specifically streaming audio */
void handle_dx( bool restart )
{
   if ( stopped == TRUE )
    return;
    
   if ( current_song == MAX_SOUND )
    return;

   static unsigned int seek_spot = 0;
   LPDIRECTSOUNDBUFFER old = song; /* keep track of the old for disposal */
   DWORD bytes = BUFFER_SIZE;
   DWORD song_bytes = sound_table[current_song].bytes;

   if ( restart == TRUE )
    seek_spot = 0;

   /* check seek for looping */
   if ( (song_bytes-seek_spot) < bytes )
    bytes = song_bytes-seek_spot; /* set bytes to the remaining few that are left */

   /* create the new one using bytes so we dont alloc more then we need */
   LPDIRECTSOUNDBUFFER d = init_buffer(bytes,current_song); /* new buf, about 2 seconds */

   if ( wave == NULL )
   {
     mmioClose( wave, 0 );
     return;
   }

   VOID* locked = NULL; /* pointer to locked buffer memory */
   DWORD lsize  = 0;    /* size of the locked buffer */

   if ( d == NULL )
   {
    mmioClose( wave, 0);
    return;
   }

   HRESULT hr;
   hr = d->lpVtbl->Lock(d, 0, bytes, &locked, &lsize, NULL, NULL, DSBLOCK_ENTIREBUFFER);

   if FAILED(hr)
   {
    delete_wave(d);
    mmioClose( wave, 0 );
    return;
   }

   /* start read in relation to the beginning */
   mmioSeek( wave, seek_spot, SEEK_SET);

   int rt = mmioRead( wave, (HPSTR)locked, (LONG)bytes );

   if ( rt == 0 )
    lsize = bytes;
   else if ( rt == -1 )
   {
    delete_wave(d);
    mmioClose( wave, 0 );
    return;
   }

   d->lpVtbl->Unlock(d, locked, lsize, NULL, 0 );

   seek_spot += bytes;

   song = d; /* song pointer is now pointing to the new bufer */

   set_volume( song, volume );
   play_wave(song,FALSE);
   

   delete_wave(old); /* get rid of the old buffer */

   ///////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\
   
   if ( bytes != BUFFER_SIZE) /* we hit the end, loop bitch */
    seek_spot = 0;
    
   evaluate_waves();
}

/* determine if the wave needs to die */
void evaluate_waves( void )
{
  SOUND *s, *snext;
  DWORD play, write;
  LPDIRECTSOUNDBUFFER d;
  
  for ( s = first_sound; s != NULL; s = snext )
  {
    snext = s->next;
    
    /* dont mess with the song */
    if ( s->sound == song || s->index == current_song )
     continue;

    d = s->sound;

    /* grab the spot at which this wave is at */
    d->lpVtbl->GetCurrentPosition(d, &play, &write);

    /* determine if its at the end */
    if ( (play >= sound_table[s->index].bytes) || (play <= 0) ) /* it is at the end */
     delete_wave(d);
  }
}
#endif