#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//
#include "proto.h"
#include "bucket.h"
#include "main.h"
#include "flash.h"
#include "utils.h"
#include "sound.h"
#include "adpcm.h"



S32 sound_create(SOUND **sound) {

  *sound = malloc(sizeof(SOUND));
  if (!*sound)               return 1;
  memset(*sound, 0, sizeof(SOUND));

  return 0;
}


S32 sound_write(SOUND *sound) {

  U32 ptr, ptr2, input, output, samples, adpcmbits;
  U8 *buf;

  // get info about formats
  output = sound->format                &255;
  input  = (sound->format>>SOUND_INPUT) &255;

  adpcmbits = 0;
  if (output == SOUNDFORMAT_ADPCM2)
    adpcmbits = 2;
  else if (output == SOUNDFORMAT_ADPCM3)
    adpcmbits = 3;
  else if (output == SOUNDFORMAT_ADPCM4)
    adpcmbits = 4;
  else if (output == SOUNDFORMAT_ADPCM5)
    adpcmbits = 5;

  // write header
  if (flush_bucket())                             return 1;
  ptr = read_position(NULL);
  if (write_ushort(0))                            return 1;
  if (write_ushort(sound->id))                    return 1;

  // write format
  if ((output == SOUNDFORMAT_LIN8) || (output == SOUNDFORMAT_LIN16)) {
    if (write_ubits(4, 0))                        return 1;

  } else if (adpcmbits) {
    if (write_ubits(4, 1))                        return 1;

  } else
    return 1;

  // write frequency
  switch (sound->freq) {
  case 5:
    if (write_ubits(2, 0))                        return 1;
    break;
  case 11:
    if (write_ubits(2, 1))                        return 1;
    break;
  case 22:
    if (write_ubits(2, 2))                        return 1;
    break;
  case 44:
    if (write_ubits(2, 3))                        return 1;
    break;
  default:
    if (write_ubits(2, 2))                        return 1;
    break;
  }

  // write no. of bits
  if (output == SOUNDFORMAT_LIN8) {
    if (write_ubits(1, 0))                        return 1;
  } else {
    if (write_ubits(1, 1))                        return 1;
  }

  // write mono/stereo
  if (sound->stereo) {
    if (write_ubits(1, 1))                        return 1;
  } else {
    if (write_ubits(1, 0))                        return 1;
  }

  if (flush_bucket())                             return 1;
  ptr2 = read_position(&buf);
  if (write_uint(0))                              return 1;

  if (sound->sizeinmemory == -1)       // make sure file is in memory
    if (load_file_into_memory(sound->file, &sound->file, &sound->sizeinmemory))
      return 1;

  // calculate no. of samples
  samples = sound->sizeinmemory;
  if (input == SOUNDFORMAT_LIN16)     samples /= 2;
  if (sound->stereo)                  samples /= 2;

  if (adpcmbits) {
    U32 n;
    ADPCMSTATE left, right;
    S16 *pshort;
    signed char *pchar;

    if (write_ubits(2, adpcmbits-2))              return 1;

    // init compressor
    adpcm_init(&left);
    if (sound->stereo)  adpcm_init(&right);
    if (input == SOUNDFORMAT_LIN16)
      pshort = (short *)sound->file;
    else
      pchar = (signed char *)sound->file;

    for (n = 0; n < samples; n++) {
      S16 s;
      S32 compressed;

      if (input == SOUNDFORMAT_LIN16)
        s = *pshort++;
      else
        s = (*pchar++)*256;

      compressed = adpcm_compress(s, &left, adpcmbits);

      if ((n & 0xfff) == 0) {
        // every 4096 samples, write a full sample
        if (write_ubits(16, s))                   return 1;
        if (write_ubits(6, left.index))           return 1;
      } else {
        if (write_ubits(adpcmbits, compressed))   return 1;
      }
      if (sound->stereo) {
        if (input == SOUNDFORMAT_LIN16)
          s = *pshort++;
        else
          s = (*pchar++)*256;

        compressed = adpcm_compress(s, &right, adpcmbits);

        if ((n & 0xfff) == 0) {
          if (write_ubits(16, s))                 return 1;
          if (write_ubits(6, right.index))        return 1;
        } else {
          if (write_ubits(adpcmbits, compressed)) return 1;
        }
      }
    }

  } else {
    U32 size;

    size = samples;
    if (sound->stereo)                  size *= 2;
    if (output == SOUNDFORMAT_LIN16)    size *= 2;
    if (write_file(sound->file, &size))    return 1;
  }

  buf[ptr2+0] = samples       & 0xff;
  buf[ptr2+1] = (samples>> 8) & 0xff;
  buf[ptr2+2] = (samples>>16) & 0xff;
  buf[ptr2+3] = (samples>>24) & 0xff;

  return update_record_header(stagDefineSound, ptr);
}


S32 write_play_sound(PLAYSOUND *play) {

  U32 ptr;

  if (flush_bucket())                             return 1;
  ptr = read_position(NULL);
  if (write_ushort(0))                            return 1;

  if (write_ushort(play->id))                     return 1;
  if (play->loops == 0) {
    if (write_ubits(4, 2))                        return 1; // stop
    if (write_ubits(1, 0))                        return 1;
    if (write_ubits(1, 0))                        return 1;
    if (write_ubits(1, 0))                        return 1;
    if (write_ubits(1, 0))                        return 1;

  } else {
    if (write_ubits(4, 0))                        return 1;
    if (write_ubits(1, 0))                        return 1;
    if (play->loops > 1) {
      if (write_ubits(1, 1))                      return 1;
    } else {
      if (write_ubits(1, 0))                      return 1;
    }
    if (write_ubits(1, 0))                        return 1;
    if (write_ubits(1, 0))                        return 1;
    if (play->loops > 1) {
      if (write_ushort(play->loops))              return 1;
    }
  }

  return update_record_header(stagStartSound, ptr);
}

