/* June 1, 1992
 * Copyright 1992 Guido van Rossum And Sundry Contributors
 * This source code is freely redistributable and may be used for
 * any purpose.  This copyright notice must be maintained.
 * Guido van Rossum And Sundry Contributors are not responsible for
 * the consequences of using this software.
 */

/*
 * "reverse" effect, uses a temporary file created by lsx_tmpfile().
 */

#include "sox_i.h"

typedef struct {
  off_t         pos;
  FILE          * tmp_file;
} priv_t;

static int start_reverse(sox_effect_t * effp)
{
  priv_t * p = (priv_t *)effp->priv;
  p->pos = 0;
  p->tmp_file = lsx_tmpfile();
  if (p->tmp_file == NULL) {
    lsx_fail("can't create temporary file: %s", strerror(errno));
    return SOX_EOF;
  }
  return SOX_SUCCESS;
}

static int flow_reverse(sox_effect_t * effp, const sox_sample_t * ibuf,
    sox_sample_t * obuf, size_t * isamp, size_t * osamp)
{
  priv_t * p = (priv_t *)effp->priv;
  if (fwrite(ibuf, sizeof(*ibuf), *isamp, p->tmp_file) != *isamp) {
    lsx_fail("error writing temporary file: %s", strerror(errno));
    return SOX_EOF;
  }
  (void)obuf, *osamp = 0; /* samples not output until drain */
  return SOX_SUCCESS;
}

static int drain_reverse(sox_effect_t * effp, sox_sample_t *obuf, size_t *osamp)
{
  priv_t * p = (priv_t *)effp->priv;
  int i, j;

  if (p->pos == 0) {
    fflush(p->tmp_file);
    p->pos = ftello(p->tmp_file);
    if (p->pos % sizeof(sox_sample_t) != 0) {
      lsx_fail("temporary file has incorrect size");
      return SOX_EOF;
    }
    p->pos /= sizeof(sox_sample_t);
  }
  p->pos -= *osamp = min((off_t)*osamp, p->pos);
  if (fseeko(p->tmp_file, (off_t)(p->pos * sizeof(sox_sample_t)), SEEK_SET) ||
      fread(obuf, sizeof(sox_sample_t), *osamp, p->tmp_file) != *osamp) {
    lsx_fail("error reading temporary file: %s", strerror(errno));
    return SOX_EOF;
  }
  for (i = 0, j = *osamp - 1; i < j; ++i, --j) { /* reverse the samples */
    sox_sample_t temp = obuf[i];
    obuf[i] = obuf[j];
    obuf[j] = temp;
  }
  return p->pos? SOX_SUCCESS : SOX_EOF;
}

static int stop_reverse(sox_effect_t * effp)
{
  priv_t * p = (priv_t *)effp->priv;
  lsx_close_tmpfile(p->tmp_file);
  return SOX_SUCCESS;
}

sox_effect_handler_t const * lsx_reverse_effect_fn(void)
{
  static sox_effect_handler_t handler = {
    "reverse", NULL, NULL, SOX_EFF_MODIFY,
    NULL, start_reverse, flow_reverse, drain_reverse, stop_reverse, NULL,
    sizeof(priv_t)
  };
  return &handler;
}
