/*
   GSPLUS - Advanced Apple IIGS Emulator Environment
   Based on the KEGS emulator written by Kent Dickey
   See COPYRIGHT.txt for Copyright information
   See LICENSE.txt for license (GPL v2)
 */

#include "SDL.h"
#include "defc.h"
#include "glog.h"
#include "sound.h"
#include <assert.h>
extern word32  *g_sound_shm_addr;
extern int g_sound_shm_pos;
extern int g_audio_enable;
extern int g_preferred_rate;

static byte *playbuf = 0;
static int g_playbuf_buffered = 0;
static SDL_AudioSpec spec;
static int snd_buf;
static int snd_write;           /* write position into playbuf */
static /* volatile */ int snd_read = 0;
static int g_sound_paused;
static int g_zeroes_buffered;
static int g_zeroes_seen;
// newer SDL allows you to specify devices.  for now, we use what it gives us,
// but this can be made configurable in the future
SDL_AudioDeviceID dev = 0;


void sdlsnd_init(word32 *shmaddr) {
  if (SDL_Init(SDL_INIT_AUDIO) < 0) {
    glog("Could not initialize SDL2 audio");
    g_audio_enable = 0;
  } else {
    glog("SDL2 audio initialized");
  }

  child_sound_loop(-1, -1, shmaddr);
  return;
}


void sound_write_sdl(int real_samps, int size) {

#ifdef HAVE_SDL
  int shm_read;

  if (real_samps) {
    shm_read = (g_sound_shm_pos - size + SOUND_SHM_SAMP_SIZE)%SOUND_SHM_SAMP_SIZE;
    SDL_LockAudioDevice(dev);
    while(size > 0) {
      if(g_playbuf_buffered >= snd_buf) {
        printf("sound_write_sdl failed @%d, %d buffered, %d samples skipped\n",snd_write,g_playbuf_buffered, size);
        shm_read += size;
        shm_read %= SOUND_SHM_SAMP_SIZE;
        size = 0;
      } else {
        ((word32*)playbuf)[snd_write/SAMPLE_CHAN_SIZE] = g_sound_shm_addr[shm_read];
        shm_read++;
        if (shm_read >= SOUND_SHM_SAMP_SIZE)
          shm_read = 0;
        snd_write += SAMPLE_CHAN_SIZE;
        if (snd_write >= snd_buf)
          snd_write = 0;
        size--;
        g_playbuf_buffered += SAMPLE_CHAN_SIZE;
      }
    }

    assert((snd_buf+snd_write - snd_read)%snd_buf == g_playbuf_buffered%snd_buf);
    assert(g_sound_shm_pos == shm_read);
    SDL_UnlockAudioDevice(dev);
  }
  if(g_sound_paused && (g_playbuf_buffered > 0)) {
    glogf("Unpausing sound, %d buffered",g_playbuf_buffered);
    g_sound_paused = 0;
    SDL_PauseAudioDevice(dev, 0);
  }
  if(!g_sound_paused && (g_playbuf_buffered <= 0)) {
    glog("Pausing sound");
    g_sound_paused = 1;
    SDL_PauseAudioDevice(dev, 1);
  }
#endif
}

#ifdef HAVE_SDL
/* Callback for sound */
static void _snd_callback(void* userdata, Uint8 *stream, int len) {
  int i;
  /* Slurp off the play buffer */
  assert((snd_buf+snd_write - snd_read)%snd_buf == g_playbuf_buffered%snd_buf);
  /*printf("slurp %d, %d buffered\n",len, g_playbuf_buffered);*/
  for(i = 0; i < len; ++i) {
    if(g_playbuf_buffered <= 0) {
      stream[i] = 0;
    } else {
      stream[i] = playbuf[snd_read++];
      if(snd_read == snd_buf)
        snd_read = 0;
      g_playbuf_buffered--;
    }
  }
#if 0
  if (g_playbuf_buffered <= 0) {
    printf("snd_callback: buffer empty, Pausing sound\n");
    g_sound_paused = 1;
    SDL_PauseAudio(1);
  }
#endif
  //printf("end slurp %d, %d buffered\n",len, g_playbuf_buffered);
}
#endif




long sound_init_device_sdl() {
#ifdef HAVE_SDL
  long rate;
  SDL_AudioSpec wanted;

  if (SDL_Init(SDL_INIT_AUDIO) < 0) {
    glogf("SDL2 Couldn't init SDL_INIT_AUDIO: %s!", SDL_GetError());
    return 0;
  }

  /* Set the desired format */
  wanted.freq = g_preferred_rate;
  wanted.format = AUDIO_S16SYS;
  wanted.channels = NUM_CHANNELS;
  wanted.samples = 512;
  wanted.callback = _snd_callback;
  wanted.userdata = NULL;

  /* Open audio, and get the real spec */
  dev = SDL_OpenAudioDevice(NULL, 0, &wanted, &spec, 0);
  if (dev == 0) {
    glogf("SDL2 Couldn't open audio: %s!", SDL_GetError());
    SDL_QuitSubSystem(SDL_INIT_AUDIO);
    return 0;

  } else {
    glogf("SDL2 opened audio device: %d", dev);
  }

  /* Check everything */
  if(spec.channels != wanted.channels) {
    glogf("SDL2 Warning, couldn't get stereo audio format!");
    //goto snd_error;
  }
  if(spec.format != wanted.format) {
    glog("SDL2 Warning, couldn't get a supported audio format!");
    glogf("SDL2 wanted %X, got %X",wanted.format,spec.format);
    //goto snd_error;
  }
  if(spec.freq != wanted.freq) {
    glogf("SDL2 wanted rate = %d, got rate = %d", wanted.freq, spec.freq);
  }
  /* Set things as they really are */
  rate = spec.freq;

  snd_buf = SOUND_SHM_SAMP_SIZE*SAMPLE_CHAN_SIZE;
  playbuf = (byte*) malloc(snd_buf);
  if (!playbuf)
    goto snd_error;
  g_playbuf_buffered = 0;

  glogf("SDL2 sound shared memory size=%d", SOUND_SHM_SAMP_SIZE * SAMPLE_CHAN_SIZE);

  g_sound_shm_addr = malloc(SOUND_SHM_SAMP_SIZE * SAMPLE_CHAN_SIZE);
  memset(g_sound_shm_addr,0,SOUND_SHM_SAMP_SIZE * SAMPLE_CHAN_SIZE);

  /* It's all good! */
  g_zeroes_buffered = 0;
  g_zeroes_seen = 0;
  /* Let's start playing sound */
  g_sound_paused = 0;
  SDL_PauseAudioDevice(dev, 0);

  set_audio_rate(rate);
  return rate;

snd_error:
  /* Oops! Something bad happened, cleanup. */
  SDL_CloseAudioDevice(dev);
  SDL_QuitSubSystem(SDL_INIT_AUDIO);
  if(playbuf)
    free((void*)playbuf);
  playbuf = 0;
  snd_buf = 0;
  return 0;
#else
  return 0;
#endif
}

void sound_shutdown_sdl() {
#ifdef HAVE_SDL
  SDL_CloseAudioDevice(dev);
  if(playbuf)
    free((void*)playbuf);
  playbuf = 0;
#endif
}
