/*
 *  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 <esd.h>
#include "context.h"

#define FACT   0.85

uint32_t options = BO_NONE;
uint32_t version = 0;

static int esdfd;                  /* file descriptor for device */

/* FIXME we could pass on polling, no ? --oliv3 */
static struct pollfd esdpfd;       /* to poll the fd */

/* audio buffer size */
static int esdabuf_size;

/* audio buffer */
static short *esdabuf;


int8_t
create(Context_t *ctx)
{
  int rate, bits, channels;
  int mode = ESD_STREAM, func = ESD_PLAY;
  char *host = NULL;
  esd_format_t format = 0;
  int server;
  esd_server_info_t *server_info;

  server=esd_open_sound(NULL);
  server_info=esd_get_server_info(server);

  if (NULL == server_info) {
    xerror("Failed to get eSound server info. Is esd running ?\n");
  }
  rate = server_info->rate;
  if ((server_info->format & ESD_MONO) == ESD_MONO) {
    channels = ESD_MONO;
  } else {
    channels = ESD_STEREO;
  }

  if ((server_info->format & ESD_BITS16) == ESD_BITS16) {
    bits = ESD_BITS16;
  } else

  {
    bits = ESD_BITS8;
  }

  printf("    >> using channels: %s\n", (channels == ESD_MONO) ? "mono" : "stereo");
  printf("    >> using resolution: %s\n", (bits == ESD_BITS8) ? "8 bits" : "16 bits");
  printf("    >> using rate: %u\n", rate);

  format = bits | channels | mode | func;

  esdfd = esd_monitor_stream( format, rate, host, NULL);

  if (esdfd <= 0) {
    xerror("Can't open socket to esd daemon \n");
  }

  esdabuf_size = Context_get_input_size(ctx);
  esdabuf = xcalloc(esdabuf_size * 2, sizeof(short));

  ctx->input = Input_new(esdabuf_size);

  esdpfd.fd = esdfd;
  esdpfd.events = POLLIN;

  okdone("ESD initialized");

  return 1;
}


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

  while (ctx->running) {
    esdpfd.revents = 0;
    poll(&esdpfd, 1, 100);

    if (esdpfd.revents & POLLIN) {
      int n, howmuch;

      howmuch = esdabuf_size * 2 * sizeof(short);
      n = read(esdfd, (void *)esdabuf, howmuch);

      if (n != howmuch) {
        xperror ("read");
      }

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

          for (uint16_t idx = 0; idx < esdabuf_size; idx++) {
            ctx->input->data[A_LEFT][idx] =
              (float)(((float)(esdabuf[n])) / (float)-SHRT_MIN);
            n++;
            ctx->input->data[A_RIGHT][idx] =
              (float)(((float)(esdabuf[n])) / (float)-SHRT_MIN);
            n++;
          }
        }

        Input_set(ctx->input, A_STEREO);
        xpthread_mutex_unlock(&ctx->input->mutex);
      }
    }
  }

  return NULL;
}


void
destroy(Context_t *ctx)
{
  Input_delete(ctx->input);
  close(esdfd);
  if (esdabuf) {
    xfree(esdabuf);
  }
}
