/*
 * audio.c
 * Copyright (C) 2002 P.Everett <peter@everett9981.freeserve.co.uk>
 *
 * This file is part of KinoAMP, a free RISCOS MPEG program stream decoder.
 *
 * KinoAMP is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * KinoAMP is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * Uses the AMPlayer module to provide an audio output from an
 * MPEG encoded audio data elementary stream input.
 *
 * v0.00 13/10/01
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "kernel.h"
#include "swis.h"
#include "timer1.h"
#include "audiosaver.h"
#include "config.h"
#include "wimp.h"
#include "ka_error.h"
#include "ka_mem.h"
#include "ka_log.h"
#include "Format_WAVE.h"

static const char *testfile = "<KinoSave$Dir>.Soundtrack";

struct audiosaver_s
{
  // save to file
  FILE*          outfile;
  uint32_t       file_type;
  struct
  {
    wave_t       wave;
    tag_t        data;
    int32_t      datasize;
  } hdr;
  const uint8_t* last_eod;
};

/**
 * Returns a new audio instance.
 */
audiosaver_t* audiosaver_new(ka_error_t* pErrorBlock)
{
  audiosaver_t* audio = ka_mem_calloc(sizeof(*audio));
  if (!audio)
  {
    // Out of memory
    ka_error_fill(pErrorBlock, ka_error_nomem);
    return NULL;
  }

  return audio;
}

/**
 * Opens the audio save file.
 * Determine the correct RISC OS file type to use.
 * In case of a WAVE file, write a WAVE header in the file.
 *
 * Returns 0 if OK, else pErrorBlock is filled.
 */
int audiosaver_openoutfile(audiosaver_t* audio, ka_error_t* pErrorBlock, const ka_aparams_t* params)
{
  if (audio->outfile || (params == NULL) || (params->typ == KA_ABLOCK_UNKNOWN))
    return -1;

  if ((audio->outfile = fopen(testfile, "wb")) == NULL)
  {
    ka_error_fill(pErrorBlock, "error10:%s", testfile); // (Cannot open Soundtrack file)
    return -1;
  }

  audio->file_type = 0xffd;
  audio->last_eod = NULL;

  switch(params->typ)
  {
    case KA_ABLOCK_MPEG:
      audio->file_type = FILETYPE_AMPEG;
    break;
    case KA_ABLOCK_AC3:
      audio->file_type = FILETYPE_AC3;
    break;
    case KA_ABLOCK_AAC:
      audio->file_type = FILETYPE_ADTS;
    break;
//    case KA_ABLOCK_LPCM_BE:
    case KA_ABLOCK_LPCM_BE_U:
//    case KA_ABLOCK_LPCM_DVD:
//    case KA_ABLOCK_LPCM_BR:
    {
      if (params->bitspersample == 8)
        audio->file_type = FILETYPE_WAVE;
    }
    break;
    case KA_ABLOCK_LPCM_LE:
    case KA_ABLOCK_LPCM_LE_U:
    {
      if (!(params->bitspersample & 7))
        audio->file_type = FILETYPE_WAVE;
    }
    break;
  }

  if (audio->file_type == FILETYPE_WAVE)
  {
    audio->hdr.wave.riff.i  = TagRIFF.i;
    audio->hdr.wave.size    = sizeof(audio->hdr) - 8;
    audio->hdr.wave.tag.i   = TagWAVE.i;
    audio->hdr.wave.fmt.i         = Tagfmt.i;
    audio->hdr.wave.fmtsize       = 16;
    audio->hdr.wave.format        = 1;
    audio->hdr.wave.channels      = params->channels;
    audio->hdr.wave.samplerate    = params->samplerate;
    audio->hdr.wave.bytespersec   = (params->bitspersample
                                  * params->channels
                                  * params->samplerate)
                                  / 8;
    audio->hdr.wave.blocksize     = params->blocksize;
    audio->hdr.wave.bitspersample = params->bitspersample;
    audio->hdr.data.i   = Tagdata.i;
    audio->hdr.datasize = 0;
    fwrite(&audio->hdr, 1, sizeof(audio->hdr), audio->outfile);
  }

  return 0;
}

/**
 * Saves an audio block in the audio save file
 */
void audiosaver_writeoutfile(audiosaver_t* audio, ka_block_t* block)
{
  if (audio->outfile && (block->eod != audio->last_eod))
  {
    fwrite(block->sod, 1, block->eod - block->sod, audio->outfile);
    audio->hdr.datasize += block->eod - block->sod;
    audio->last_eod = block->eod;
  }
}

/**
 * Closes the possibly audio save file.
 * Give it the correct RISC OS file type.
 * In case of a WAVE file, update the sizes in the WAVE header first.
 *
 * Returns a negative value in case or error
 */
int audiosaver_closeoutfile(audiosaver_t* audio)
{
  if (audio->outfile)
  {
    if (audio->file_type == FILETYPE_WAVE)
    {
      // Fix original WAV header
      fseek(audio->outfile, 0, SEEK_SET);
      audio->hdr.wave.size += audio->hdr.datasize;
      fwrite(&audio->hdr, 1, sizeof(audio->hdr), audio->outfile);
    }
    fclose(audio->outfile);
    audio->outfile = NULL;

    _swix(OS_File, _INR(0,2), WRITE_CATINFO, testfile, audio->file_type);

    return 0;
  }

  return -1;
}

int audiosaver_issaving(audiosaver_t* audio)
{
  return (audio->outfile != NULL);
}

/**
 * Releases an audio instance.
 *
 * @param  audio    A pointer to an audio instance.
 */
void audiosaver_delete(audiosaver_t** paudio)
{
  audiosaver_t* audio = *paudio;
  *paudio = NULL;

  if (!audio)
    return;

  audiosaver_closeoutfile(audio);

  ka_mem_free(audio);
}
