/**********************************************/
/* Reusable sound and music routines          */
/* Evert Glebbeek 2002, 2003                  */
/* eglebbk@phys.uva.nl                        */
/**********************************************/
#include <allegro.h>
#include <stdio.h>
#include <string.h>
#include "global.h"
#include "sound.h"
#include "str.h"
#include "cd.h"

#define MAX_ALIASES 16
#define MAX_SAMPLE_ALIASES 16

typedef struct {
   int tracknr;
   char *filename;
} MUSIC_ALIAS;

typedef struct {
   char *filename;
} SAMPLE_ALIAS;


static int sound_init = FALSE;
static int sound_flags = MIDI_MUSIC;
MIDI *cmusic = NULL;
SAMPLE **csample = NULL;
static int digi_vol = 255;
static int midi_vol = 255;
static int cd_vol = 128;
static int pan = 128;

static MUSIC_ALIAS *music_aliases = NULL;
static SAMPLE_ALIAS *sample_aliases = NULL;
static int music_num_aliases = 0;
static int sample_num_aliases = 0;

static int max_samples = 8;
static int sample_slot = 0;

/* Initialize sound effects */
/* Returs 0 on success */
int set_sound(void)
{
   int res;
   int n;

   /* Exit if we don't want any sound */
   if (!(settings.flags & (MUSIC_MIDI + MUSIC_CD + SFX_DIGI)))
      return 0;
   /* Check CD sound */
#ifndef DONT_USE_CD
   if (cd_init() == 0) {
      sound_flags |= SOUND_CD;
      sound_init = TRUE;
   }
#endif
   if (detect_midi_driver(MIDI_AUTODETECT)) {
      sound_flags |= SOUND_MIDI;
   }
   if (detect_digi_driver(DIGI_AUTODETECT))
      sound_flags |= SOUND_DIGI;

   atexit(stop_sound);
   res = install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, NULL);
   if (res) {
      fprintf(stderr, "Error initialising sound: %s\n", allegro_error);
   }
   sound_init = sound_init || (!res);

   set_midi_volume(midi_vol);
   set_digi_volume(digi_vol);
   set_cd_volume(cd_vol);

   /* Read global settings */
   select_music(NONE);
   if (settings.flags & MUSIC_MIDI)
      select_music(MIDI_MUSIC);
   else if (settings.flags & MUSIC_CD)
      select_music(CD_MUSIC);

   if (sound_flags & SOUND_DIGI) {
      if (csample) {
         for (n = 0; n < max_samples; n++) {
            if (csample[n]) {
               destroy_sample(csample[n]);
               csample[n] = NULL;
            }
         }
      } else {
         csample = malloc(max_samples * sizeof(SAMPLE *));
         for (n = 0; n < max_samples; n++)
            csample[n] = NULL;
         sample_slot = 0;
      }
   }

   return sound_init;
}

/* Read music aliases from a disk file */
void read_music_aliases(const char *filename)
{
   char *s = malloc(1024);
   char *sep;
   PACKFILE *f;
   int n;
   int in_section = FALSE;

   music_aliases = realloc(music_aliases, MAX_ALIASES * sizeof(MUSIC_ALIAS));
   music_num_aliases = 0;
   for (n = 0; n < MAX_ALIASES; n++) {
      music_aliases[n].filename = NULL;
      music_aliases[n].tracknr = 0;
   }

   f = pack_fopen(filename, "r");

   if (!f) {
      allegro_message("Cannot load %s!\n", filename);
      exit(EXIT_FAILURE);
   }

   while (pack_fgets(s, 1024, f)) {
      sep = strstr(s, "\n");
      if (sep)
         sep[0] = '\0';

      sep = strstr(s, ";");
      if (sep)
         sep[0] = '\0';

      if (s[0]) {
         if (!strcmp(s, "[music]")) {
            in_section = TRUE;
         } else if (s[0] == '[') {
            in_section = FALSE;
         } else if (in_section) {
            sscanf(s, "%d", &n);
            music_aliases[music_num_aliases].tracknr = n;
            sep = strflushl(strstr(s, " "));
            if (sep) {
               music_aliases[music_num_aliases].filename = strdup(sep);
            }
            music_num_aliases++;
         }
      }
   }

   pack_fclose(f);

   free(s);
}

/* Read sample aliases from a disk file */
void read_sample_aliases(const char *filename)
{
   char *s = malloc(1024);
   char *sep;
   PACKFILE *f;
   int n;
   int in_section = FALSE;

   sample_aliases =
      realloc(sample_aliases, MAX_SAMPLE_ALIASES * sizeof(SAMPLE_ALIAS));
   sample_num_aliases = 0;
   for (n = 0; n < MAX_SAMPLE_ALIASES; n++) {
      sample_aliases[n].filename = NULL;
   }

   f = pack_fopen(filename, "r");

   while (pack_fgets(s, 1024, f)) {
      sep = strstr(s, "\n");
      if (sep)
         sep[0] = '\0';

      sep = strstr(s, ";");
      if (sep)
         sep[0] = '\0';

      if (s[0]) {
         if (!strcmp(s, "[sample]")) {
            in_section = TRUE;
         } else if (s[0] == '[') {
            in_section = FALSE;
         } else if (in_section) {
            sscanf(s, "%d", &n);
            sep = strflushl(strstr(s, " "));
            if (sep)
               sample_aliases[sample_num_aliases].filename = strdup(sep);
            sample_num_aliases++;
         }
      }
   }

   pack_fclose(f);

   free(s);
}

/* Get sound-initialization flags */
int read_sound_flags(void)
{
   return sound_flags;
}

/* Select music type */
void select_music(const int type)
{
   if (type == CD_MUSIC) {
      if (sound_flags & SOUND_CD) {
         sound_flags |= MIDI_MUSIC;
         sound_flags |= CD_MUSIC;
         sound_flags ^= MIDI_MUSIC;
      }
   } else if (type == MIDI_MUSIC) {
      if (sound_flags & SOUND_MIDI) {
         sound_flags |= CD_MUSIC;
         sound_flags |= MIDI_MUSIC;
         sound_flags ^= CD_MUSIC;
      }
   } else if (type == NONE) {
      sound_flags |= CD_MUSIC + MIDI_MUSIC;
      sound_flags ^= CD_MUSIC + MIDI_MUSIC;
   }
}

void set_digi_volume(const int vol)
{
   digi_vol = vol;
   set_volume(vol, -1);
}

void set_midi_volume(const int vol)
{
   midi_vol = vol;
   set_volume(-1, vol);
}

void set_cd_volume(const int vol)
{
   cd_vol = vol;
#ifndef DONT_USE_CD
   cd_set_volume(vol, vol);
#endif
}

void set_sfx_volume(const int vol)
{
   set_digi_volume(vol);
}

void set_music_volume(const int vol)
{
   if (sound_flags & MIDI_MUSIC) {
      set_midi_volume(vol);
   } else {
      set_cd_volume(vol);
   }
}

inline int get_midi_volume(void)
{
   return midi_vol;
}

inline int get_cd_volume(void)
{
   return cd_vol;
}

inline int get_digi_volume(void)
{
   return digi_vol;
}

inline int get_sfx_volume(void)
{
   return get_digi_volume();
}

inline int get_music_volume(void)
{
   if (sound_flags & MIDI_MUSIC) {
      return get_midi_volume();
   } else {
      return get_cd_volume();
   }
}

/* Start music-ID number something */
/* ID numbers must be translated to files/tracks or whatever */
void start_music(const int id, const int looped)
{
   if (!sound_init)
      return;
   if (id >= MAX_ALIASES)
      return;
   if (sound_flags & CD_MUSIC) {
#ifndef DONT_USE_CD
      if (music_aliases[id].tracknr) {
         cd_play(music_aliases[id].tracknr);
      } else {
         cd_stop();
      }
#endif
   } else if (sound_flags & MIDI_MUSIC) {
      if (cmusic) {
         destroy_midi(cmusic);
         cmusic = NULL;
      }
      if (music_aliases[id].filename) {
         cmusic = load_midi(music_aliases[id].filename);
      } else {
         cmusic = NULL;
      }
      play_midi(cmusic, looped);
   }
}

/* There are eight slots for samples, and for each only one sample can be active */
void start_sample(const int id, const int looped)
{
   if (!sound_init)
      return;
   if (id >= MAX_SAMPLE_ALIASES)
      return;
   if (id < 0)
      return;

   if (csample[sample_slot]) {
      destroy_sample(csample[sample_slot]);
      csample[sample_slot] = NULL;
   }
   if (sample_aliases[id].filename) {
      csample[sample_slot] = load_sample(sample_aliases[id].filename);
   } else {
      csample[sample_slot] = NULL;
   }
   if (csample[sample_slot]) {
      play_sample(csample[sample_slot], digi_vol, pan, 1000, looped);
   }

   sample_slot++;
   sample_slot %= max_samples;
}

/* Returns TRUE if a CD track is currently playing */
int cd_playing(void)
{
#ifndef DONT_USE_CD
   return cd_current_track();
#else
   return FALSE;
#endif
}

/* Returns TRUE if midi is currently playing */
int midi_playing(void)
{
   return midi_pos >= 0;
}

/* Returns TRUE if background music is currently playing */
int music_playing(void)
{
   if (sound_flags & CD_MUSIC) {
      return cd_playing();
   } else if (sound_flags & MIDI_MUSIC) {
      return midi_playing();
   }
   return FALSE;
}

/* Halts playback of background music */
inline void stop_music(void)
{
#ifndef DONT_USE_CD
   cd_stop();
#endif
   stop_midi();
}

inline void stop_sfx(void)
{
   int c;

   if (csample)
      for (c = 0; c < max_samples; c++)
         if (csample[c]) {
            destroy_sample(csample[c]);
            csample[c] = NULL;
         }
}

/* Halt all playback: sounds and music */
void stop_sound(void)
{
   stop_sfx();
   stop_music();
}
