/*
  ogmmerge -- utility for splicing together ogg bitstreams
      from component media subtypes

  r_wav.cpp
  WAVE demultiplexer module (supports raw / uncompressed PCM audio only,
  type 0x0001)

  Written by Moritz Bunkus <moritz@bunkus.org>
  Based on Xiph.org's 'oggmerge' found in their CVS repository
  See http://www.xiph.org

  Distributed under the GPL
  see the file COPYING for details
  or visit http://www.gnu.org/copyleft/gpl.html
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <ogg/ogg.h>

extern "C" {
#include <avilib.h> // for wave_header
}

#include "ogmmerge.h"
#include "ogmstreams.h"
#include "queue.h"
#include "r_wav.h"

int wav_reader_c::probe_file(FILE *file, off_t size) {
  wave_header wheader;
  if (size < sizeof(wave_header))
    return 0;
  if (fseeko(file, 0, SEEK_SET) != 0)
    return 0;
  if (fread((char *)&wheader, 1, sizeof(wheader), file) != sizeof(wheader)) {
    fseeko(file, 0, SEEK_SET);
    return 0;
  }
  fseeko(file, 0, SEEK_SET);
  if (strncmp((char *)wheader.riff.id, "RIFF", 4) ||
      strncmp((char *)wheader.riff.wave_id, "WAVE", 4) ||
      strncmp((char *)wheader.data.id, "data", 4))
    return 0;
  return 1;    
}

wav_reader_c::wav_reader_c(char *fname, audio_sync_t *nasync,
                           range_t *nrange, char **ncomments) throw (error_c) {
  uint64_t size;
  uint32_t samplerate;
  uint16_t channels, bitdepth;
  
  if ((file = fopen(fname, "r")) == NULL)
    throw error_c("wav_reader: Could not open source file.");
  if (fseeko(file, 0, SEEK_END) != 0)
    throw error_c("wav_reader: Could not seek to end of file.");
  size = ftello(file);
  if (fseeko(file, 0, SEEK_SET) != 0)
    throw error_c("wav_reader: Could not seek to beginning of file.");
  if (!wav_reader_c::probe_file(file, size))
    throw error_c("wav_reader: Source is not a valid WAVE file.");
  if (fread(&wheader, 1, sizeof(wheader), file) != sizeof(wheader))
    throw error_c("wav_reader: could not read WAVE header.");
  bps = get_uint16(&wheader.common.wChannels) *
    get_uint16(&wheader.common.wBitsPerSample) *
    get_uint32(&wheader.common.dwSamplesPerSec) / 8;
  chunk = (unsigned char *)malloc(bps + 1);
  if (chunk == NULL)
    die("malloc");
  bytes_processed = 0;
  samplerate = get_uint32(&wheader.common.dwSamplesPerSec);
  channels = get_uint16(&wheader.common.wChannels);
  bitdepth = get_uint16(&wheader.common.wBitsPerSample);
  pcmpacketizer = new pcm_packetizer_c(samplerate, channels, bitdepth, nasync,
                                       nrange, ncomments);
  if (verbose)
    fprintf(stderr, "Using WAV demultiplexer for %s.\n+-> Using " \
            "PCM output module for audio stream.\n", fname);
}

wav_reader_c::~wav_reader_c() {
  if (file != NULL)
    fclose(file);
  if (chunk != NULL)
    free(chunk);
  if (pcmpacketizer != NULL)
    delete pcmpacketizer;
}

int wav_reader_c::read() {
  int nread, last_frame;
    
  if (pcmpacketizer->page_available())
    return EMOREDATA;
  
  nread = fread(chunk, 1, bps, file);
  if (nread <= 0) {
/*
 * In case that the WAVE header is not set correctly or any other error
 * occurs. The 'normal' end-of-stream should be handled by comparing the
 * number of bytes read to the WAVE header fields.
 */
    *chunk = 0;
    pcmpacketizer->process((char *)chunk, 1, 1);
    pcmpacketizer->flush_pages();
    return 0;
  }
  last_frame = 0;
  if ((bytes_processed + nread) >=
      (get_uint32(&wheader.riff.len) - sizeof(wave_header) + 8))
    last_frame = 1;
  pcmpacketizer->process((char *)chunk, nread, last_frame);
  bytes_processed += nread;

  if (last_frame)
    return 0;
  else
    return EMOREDATA;
}

int wav_reader_c::serial_in_use(int serial) {
  return pcmpacketizer->serial_in_use(serial);
}

ogmmerge_page_t *wav_reader_c::get_header_page(int header_type) {
  return pcmpacketizer->get_header_page(header_type);
}

ogmmerge_page_t *wav_reader_c::get_page() {
  return pcmpacketizer->get_page();
}

int wav_reader_c::display_priority() {
  return DISPLAYPRIORITY_HIGH - 1;
}

void wav_reader_c::reset() {
  if (pcmpacketizer != NULL)
    pcmpacketizer->reset();
}

void wav_reader_c::display_progress() {
  int samples = (get_uint32(&wheader.riff.len) - sizeof(wheader) + 8) / bps;
  fprintf(stdout, "progress: %d/%d seconds (%d%%)\r",
          (int)(bytes_processed / bps), (int)samples,
          (int)(bytes_processed * 100L / bps / samples));
  fflush(stdout);
}

