/* generic.c */

/* In this code I refer to the buffers used by the codec as "sample buffers",
   and buffers used by this code to buffer data from the file as "file buffers".
*/

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

#include "swis.h"

#include "errors.h"
#include "generic.h"
#include "log.h"
#include "mstream.h"
#include "osfstream.h"
#include "replay.h"
#include "xstr.h"

int
generic_build_chunk_table(long int offset, size_t size)
{
  int chunk;
  int rem;
  replay_master.nchunks = size / GENERIC_CHUNK_SIZE;
  rem = size % GENERIC_CHUNK_SIZE;
  if (rem)
    ++replay_master.nchunks;
  LOG(("Building chunk table for %d chunks\n", replay_master.nchunks));
  replay_master.chunks = malloc(replay_master.nchunks
                                * sizeof(replay_chunk_data));
  if (!replay_master.chunks)
    return EOF;
  for (chunk = 0; chunk < replay_master.nchunks; ++chunk)
  {
    replay_master.chunks[chunk].offset = offset
                                   + (long int) chunk * GENERIC_CHUNK_SIZE;
    replay_master.chunks[chunk].size = GENERIC_CHUNK_SIZE;
  }
  if (rem)
    replay_master.chunks[replay_master.nchunks - 1].size = rem;
  return 0;
}

_kernel_oserror *
generic_prepare_already_open(size_t size, long int offset,
                             int from_file, const char *codec, int *chunk_size)
{
  if (from_file)
  {
    replay_master.read_line = osfstream_read_line;
    replay_master.close = osfstream_close;
    replay_master.seek = osfstream_seek;
    replay_master.read_bytes = osfstream_read_bytes;
  }
  else
  {
    replay_master.read_line = mstream_read_line;
    replay_master.close = mstream_close;
    replay_master.seek = mstream_seek;
    replay_master.read_bytes = mstream_read_bytes;
  }
  replay_master.decomp = xstrdup(codec);
  if (!replay_master.decomp)
  {
    replay_remove_current();
    return &error_memory;
  }
  LOG(("generic_prepare_already_open preparing for use with codec %s\n",
       replay_master.decomp));
  if (generic_build_chunk_table(offset, size))
  {
    replay_remove_current();
    return &error_memory;
  }
  *chunk_size = GENERIC_CHUNK_SIZE;
  return 0;
}

_kernel_oserror *
generic_prepare(const char *file, size_t size, long int offset,
                const char *codec, int from_file,
                replay_handle *handle_out, int *chunk_size)
{
  _kernel_oserror *e;
  LOG(("\n\nLoading new generic file\n"));
  replay_remove_current();
  e = replay_get_handle(handle_out);
  if (e)
    return e;
  if (from_file)
  {
    replay_master.fm.fp = osfstream_open(file);
    if (!replay_master.fm.fp)
      return &error_bad_replay;
  }
  else
  {
    replay_master.fm.mp = mstream_open(file, size);
    if (!replay_master.fm.mp)
      return &error_memory;
  }
  return generic_prepare_already_open(size, offset, from_file,
                                      codec, chunk_size);
}
