/*
 *  Copyright 1994-2022 Olivier Girondel
 *
 *  This file is part of lebiniou.
 *
 *  lebiniou 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.
 *
 *  lebiniou 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 lebiniou. If not, see <http://www.gnu.org/licenses/>.
 */

#include <sndio.h>
#include "context.h"
#include "pthread_utils.h"


uint32_t options = BO_NONE;
uint32_t version = 0;


static unsigned char *buf;
static struct sio_par par;
static struct sio_hdl *hdl;
static size_t bufsz;
static ssize_t n;

static unsigned long long recpos = 0, readpos = 0;;

static uint32_t frames;


static void
cb(void *addr, int delta)
{
  Context_t *ctx = (Context_t *)addr;

  if (NULL != ctx->input) {
    unsigned int bytes;

    bytes = delta * par.bps * par.rchan;
    recpos += bytes;

    if (!ctx->input->mute) {
      if (!xpthread_mutex_lock(&ctx->input->mutex)) {
        uint16_t n = 0;

        for (uint16_t idx = 0; idx < frames; idx++) {
          ctx->input->data[A_LEFT][idx] =
            (float)(((float)(buf[n])) / (float)-SHRT_MIN);
          n++;
          ctx->input->data[A_RIGHT][idx] =
            (float)(((float)(buf[n])) / (float)-SHRT_MIN);
          n++;
        }
        Input_set(ctx->input, A_STEREO);
        xpthread_mutex_unlock(&ctx->input->mutex);
      }
    }
  } else {
    fprintf(stderr, "%s: no input. Is sndiod running ?\n", __FILE__);
    exit(1);
  }
}


int8_t
create(Context_t *ctx)
{
  frames = Context_get_input_size(ctx);
  uint32_t size = frames * 2; /* 2 channels */

  sio_initpar(&par);
  par.sig = 1;
  par.bits = 16;
  par.rchan = 2;
  par.rate = 44100;
  par.appbufsz = size;

  hdl = sio_open(SIO_DEVANY, SIO_REC, 0);
  if (NULL == hdl) {
    fprintf(stderr, "[!] %s: sio_open() failed\n", __FILE__);
    return 0;
  }
  sio_onmove(hdl, cb, ctx);
  if (!sio_setpar(hdl, &par)) {
    fprintf(stderr, "[!] %s: sio_setpar() failed\n", __FILE__);
    return 0;
  }
  if (!sio_getpar(hdl, &par)) {
    fprintf(stderr, "[!] %s: sio_getpar() failed\n", __FILE__);
    return 0;
  }
  bufsz = par.bps * par.rchan * par.round;
  buf = xmalloc(bufsz);
  printf("[i] %s: %zu bytes buffer, max latency %u frames\n",
         __FILE__, bufsz, par.bufsz);
  printf("[i] %s: bps %d rchan %d round %d bufsz %zu\n", __FILE__, par.bps, par.rchan, par.round, bufsz);
  if (!sio_start(hdl)) {
    fprintf(stderr, "[!] %s: sio_start() failed\n", __FILE__);
    return 0;
  }

  ctx->input = Input_new(frames);

  return 1;
}


void *
jthread(void *args)
{
  Context_t *ctx = (Context_t *)args;

  while (ctx->running) {
    n = bufsz;
    n = sio_read(hdl, buf, n);
    if (n == 0) {
      xerror("%s: sio_read: failed\n", __FILE__);
    }
    readpos += n;
  }

  return NULL;
}


void
destroy(Context_t *ctx)
{
  sio_close(hdl);
  Input_delete(ctx->input);
  xfree(buf);
}
