#include "sasound.h"
/* Because we are not ready yet to release this password... */
#include <allegro.h> // to load the packfile...
#include "savegame.h" // to be able to save sample status
#include "emudx.h"
#include "newmem.h"
#include <string.h>
#include "dxsmp.h"
#include "streams.h"

/* DX Samples : my first idea was to add these in sasound.c and to mix them after
   the normal streams have been processed. Advantage : code very easy.
   Disadvantages : 1 version/sound driver, and not precise enough where you must have
   a big buffer for sdl (buggy sound driver in windows).
   So here is the "generic" implementation. But it's not so generic : allegro is missing
   sample conversion functions (in fact it probably has them internally). So I can't
   convert the samples with allegro as easily, unless I copy the conversion code from
   the sdl source... For now allegro is used only in dos, so it does not seem very
   important. */

#ifndef ALLEGRO_SOUND

#include <SDL.h> // ... and to convert the samples !!!

struct wave_spec {
  // to be saved
  int      pos;		/* Current play position */
  int volume,playing,loop;
};

struct private_data {
  int len;
  UINT8 *data;
};

static struct wave_spec* emudx_samples;
static struct private_data* sample_data;

// to be able to save them easily

int raine_nb_samples;

extern SDL_AudioSpec gotspec;

static char dx_file[256];

void read_dx_file() {
  int i;
  SDL_AudioCVT  wav_cvt;
  SDL_AudioSpec wave;
  SAMPLE *sample;
  DATAFILE *dat;
/*   int start,end; */
/*   short *src; */

  if (!dx_file[0])
    return;
  if (raine_nb_samples && emudx_samples && sample_data) {
    return;
  }

  set_emudx_password();
  dat = load_datafile(dx_file);
  packfile_password("");

  raine_nb_samples = 1;
  while (dat[raine_nb_samples].dat && dat[raine_nb_samples].type == ASCII_ID('S','A','M','P')) {
    raine_nb_samples++;
  }
  raine_nb_samples--;

  emudx_samples = (struct wave_spec*)AllocateMem((raine_nb_samples+1) * sizeof(struct wave_spec));
  AddSaveData(ASCII_ID('S','A','M','P'),
	      (UINT8*)emudx_samples,
	      (raine_nb_samples+1) * sizeof(struct wave_spec));

  sample_data = (struct private_data*)AllocateMem((raine_nb_samples+1)*sizeof(struct private_data));
  for (i=1; i<=raine_nb_samples; i++) {
    int len_in_bytes;
    sample = (SAMPLE *)(dat[i].dat);

    wave.freq = sample->freq;
    wave.format = (sample->bits == 16 ? AUDIO_U16 : AUDIO_U8);
    wave.samples= sample->len;
    wave.callback=NULL;
    wave.userdata = NULL;
    wave.channels = (sample->stereo ? 2 : 1);
    wave.silence = 0;

    if (SDL_BuildAudioCVT(&wav_cvt,
			  wave.format, wave.channels, wave.freq,
			  gotspec.format, gotspec.channels,gotspec.freq) == -1) {
      printf("can't build converter\n");
      exit(1);
    }

    len_in_bytes = sample->len * (sample->bits == 16 ? 2 : 1) * (sample->stereo ? 2 : 1);
    wav_cvt.buf = sample_data[i].data = AllocateMem(len_in_bytes*wav_cvt.len_mult);
    sample_data[i].len =len_in_bytes*wav_cvt.len_ratio;
    wav_cvt.len = len_in_bytes;
    memcpy(sample_data[i].data, sample->data, len_in_bytes);
    emudx_samples[i].pos = emudx_samples[i].playing = 0;

    SDL_ConvertAudio(&wav_cvt);
    // cuting silences at the start and end of samples : does not work for dkong because
    // of the walk sample : it started 3 frames in a row and then 10 frames later.
    // so you must have at least one frame of silence at then begining of the sound !!!

    /*       start = 0; */
    /*       end = emudx_samples[i].len/2-1; */
    /*       src = (short*)wav_cvt.buf; */
    /*       while (!src[start]) */
    /* 	start++; */
    /*       while (!src[end]) */
    /* 	end--; */
    /*       if (start >= 2) */
    /* 	start -= 2; */
    /*       printf("sample %d start %d end %d total len %d durée %g (%g)\n",i,start,emudx_samples[i].len/2-1-end,emudx_samples[i].len/2,sample->len*1.0/sample->freq,len_in_bytes*1.0/44100/2/2); */
    /*       emudx_samples[i].len -= (start+(emudx_samples[i].len/2-1-end))*2; */
    /*       memmove(src,&src[start],emudx_samples[i].len); */
  }
  unload_datafile(dat);
}

static void dxsmp_update(int num, INT16 **buffer, int length)
{
  int nb;

  memset(buffer[0],0,length*2);
  memset(buffer[1],0,length*2);

  for (nb=1; nb<=raine_nb_samples; nb++) { // samples playing ?
    if (emudx_samples[nb].playing) {
      int i,len2 = length*4; // len = length in bytes
      signed short *din = (signed short *)&sample_data[nb].data[emudx_samples[nb].pos];
      int volume = emudx_samples[nb].volume;
      // printf("playing sample %d pos %d\n",nb,emudx_samples[nb].pos);
      if (emudx_samples[nb].pos + len2 > sample_data[nb].len) {
	len2 = sample_data[nb].len-emudx_samples[nb].pos;
	emudx_samples[nb].playing = 0;
      }
      emudx_samples[nb].pos += len2;
      if (!emudx_samples[nb].playing && emudx_samples[nb].loop) {
	emudx_samples[nb].playing = 1;
	emudx_samples[nb].pos = 0;
      }
      len2 /= 4;
      for (i=0; i<len2; i++) {
	INT16 left = *(din++)*volume/255;
	INT16 right = *(din++)*volume/255;
#ifdef TEST_OVERFLOW
	INT32 sample = buffer[0][i]+left;
	if (sample > 0x7fff) {
	  printf("overflow left %x\n",sample);
	  sample = 0x7fff;
	} else if (sample < -0x8000) {
	  printf("underflow left %x\n",sample);
	  sample = -0x8000;
	}
	buffer[0][i] += sample;
	sample = buffer[1][i] + right;
	if (sample > 0x7fff) {
	  printf("overflow right %x\n",sample);
	  sample = 0x7fff;
	} else if (sample < -0x8000) {
	  printf("underflow right %x\n",sample);
	  sample = -0x8000;
	}
	buffer[1][i] += sample;
#else
	buffer[0][i] += left;
	buffer[1][i] += right;
#endif
      }
    }
  }
}


void raine_stop_sample(int sample) {
  if (sample > 0 && sample <= raine_nb_samples) {
    emudx_samples[sample].playing = 0;
  }
}

void raine_stop_samples() {
  int i;
  for (i=1; i<= raine_nb_samples; i++)
    emudx_samples[i].playing = 0;
}

void raine_play_sample(int sample, int volume) {
  if (sample > 0 && sample <= raine_nb_samples) {
    emudx_samples[sample].pos = 0;
    emudx_samples[sample].volume = volume;
    emudx_samples[sample].playing = 1;
    emudx_samples[sample].loop = 0;
  }
}

void raine_loop_sample(int sample, int volume) {
  if (sample > 0 && sample <= raine_nb_samples) {
    emudx_samples[sample].pos = 0;
    emudx_samples[sample].volume = volume;
    emudx_samples[sample].playing = 1;
    emudx_samples[sample].loop = 1;
  }
}

int dxsmp_sh_start(const struct dxsmpinterface *intf) {
  char *str;
  const char *stream_names[2] = { "DX Sample L", "DX Sample R"};

  if ((str = exists_emudx_file(intf->dx_name))) {
    DATAFILE *dat;
    int vol[2];
    vol[0] = intf->mixing_level & 0xffff;
    vol[1] = intf->mixing_level >> 16;

    set_emudx_password();
    dat = load_datafile(str);
    packfile_password("");

    if (!dat) {
      raine_nb_samples = 0;
      return 1;
    }
    unload_datafile(dat);
    strcpy(dx_file,str);
    if (stream_init_multim(2, stream_names, vol, audio_sample_rate, 0, dxsmp_update) == -1)
      return 1;

    return 0;
  }
  return 1; // failure
}

void dxsmp_sh_stop() {
  int i;
  if (raine_nb_samples && emudx_samples) {
    for (i=1; i<=raine_nb_samples; i++) {
      FreeMem(sample_data[i].data);
    }
    FreeMem((UINT8*)emudx_samples);
    FreeMem((UINT8*)sample_data);
    sample_data = NULL;
    emudx_samples = NULL;
    raine_nb_samples = 0;
  }
  dx_file[0] = 0;
}

int is_dxsmp_playing(int sample) {
  return emudx_samples[sample].playing;
}
#else

// Allegro version, much less precise because I have no conversion functions here !!!

static DATAFILE *dat;

int raine_nb_samples;

int dxsmp_sh_start(const struct dxsmpinterface *intf) {
  char *str;

  if ((str = exists_emudx_file(intf->dx_name))) {
    set_emudx_password();
    dat = load_datafile(str);
    packfile_password("");
    if (dat) {
      raine_nb_samples = 1;
      while (dat[raine_nb_samples].dat && dat[raine_nb_samples].type == ASCII_ID('S','A','M','P')) {
	raine_nb_samples++;
      }
      raine_nb_samples--;

      return 0;
    }
  }
  return 1; // failure
}

void dxsmp_sh_stop() {
  raine_stop_samples();
  raine_nb_samples = 0;
  unload_datafile(dat);
  dat = NULL;
}

void raine_stop_sample(int sample) {
  if (sample > 0 && sample <= raine_nb_samples) {
    stop_sample(dat[sample].dat);
  }
}

void raine_stop_samples() {
  int i;
  for (i=1; i<= raine_nb_samples; i++)
    stop_sample(dat[i].dat);
}

int is_dxsmp_playing(int sample) {
  return 0; // can't say this in allegro !!!
}

void raine_play_sample(int sample, int volume) {
  play_sample(dat[sample].dat,volume,128,1000,FALSE);
}

void raine_loop_sample(int sample, int volume) {
  play_sample(dat[sample].dat,volume,128,1000,TRUE);
}
#endif
