/* CSL - Common Sound Layer
 * Copyright (C) 2000-2001 Stefan Westerfeld and Tim Janik
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#include	"artsdriver.h"
#include	"artssoundserver.h"
#include	"artscsladapter.h"
#include	"cslprivate.h"
#include	"cslutils.h"

#include	<stdlib.h>
#include	<string.h>
#include	"artsmd5.h"

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


/* --- typedefs & structures --- */

typedef struct {
  CslPcmStream  stream;
  int           id;
  unsigned int  last_buffer_level;
} ArtsPcmStream;

struct _CslPcmDriver
{
  ArtsMcopDispatcher *dispatcher;
  ArtsSoundServer    *server;
  ArtsCslAdapter     *adapter;
  int n_streams;
};

#define	ARTS_PCM_STREAM(pcmstream)	((ArtsPcmStream*) (pcmstream))

struct _CslSampleDriver
{
  int n_samples;
};

typedef struct {
  CslSample  sample;
  int        id;
} ArtsSample;

#define	ARTS_SAMPLE(sample)		((ArtsSample*) (sample))

/* --- prototypes --- */
static CslErrorType  arts_pcm_driver_init		(CslDriver       *driver);
static void	     arts_pcm_driver_shutdown		(CslDriver       *driver);
static CslErrorType  arts_pcm_stream_init		(CslDriver       *driver,
							 const char      *role,
							 unsigned int     rate,
							 unsigned int     n_channels,
							 CslPcmFormatType format,
							 CslBool	  readable,
							 CslPcmStream	**stream_p);
static void	     arts_pcm_stream_destroy            (CslPcmStream    *stream);
static int	     arts_pcm_read               	(CslPcmStream    *stream,
							 unsigned int     n_bytes,
							 char            *bytes);
static int	     arts_pcm_write              	(CslPcmStream    *stream,
							 unsigned int     n_bytes,
							 char            *bytes);
static CslErrorType  arts_pcm_update_status         	(CslPcmStream    *stream);
static CslErrorType  arts_pcm_sync               	(CslPcmStream    *stream);
static CslErrorType  arts_pcm_flush              	(CslPcmStream    *stream);
static CslErrorType  arts_pcm_set_title          	(CslPcmStream   *stream,
							 const char     *title);
static CslErrorType  arts_pcm_set_stream_mode    	(CslPcmStream   *stream,
							 unsigned int    buffer_size,
							 unsigned int    byte_watermark);
static CslErrorType  arts_pcm_set_packet_mode		(CslPcmStream   *stream,
							 unsigned int	 n_packets,
							 unsigned int	 packet_size,
							 unsigned int	 packet_watermark);
static CslErrorType  arts_pcm_set_channel_mapping       (CslPcmStream   *stream,
							 unsigned int    channel,
							 const char     *mapping);
static CslErrorType  pcm_device_arts_setup		(ArtsPcmStream	*stream);

static CslErrorType	arts_sample_driver_init	        (CslDriver	*driver);
static void		arts_sample_driver_shutdown     (CslDriver	*driver);

static CslErrorType	arts_sample_new		        (CslDriver	*driver,
					                 const char     *name,
					                 const char     *role,
					                 const char     *title,
							 CslSample     **sample_p);
static void		arts_sample_release		(CslSample	*sample);

static int		arts_sample_write	        (CslSample	*sample,
				 	                 unsigned int    n_bytes,
				 	                 void           *bytes);
static CslErrorType	arts_sample_write_done	        (CslSample	*sample);

static CslErrorType	arts_sample_play		(CslSample	*sample);
static CslBool		arts_sample_is_playing		(CslSample	*sample);
static CslErrorType	arts_sample_stop		(CslSample	*sample);
static CslErrorType	arts_sample_wait_done		(CslSample	*sample);

static CslErrorType	arts_sample_cache_add	        (CslSample	*sample);
static CslErrorType	arts_sample_cache_find	        (CslDriver      *driver,
					                 const char     *name,
					                 const char     *role,
					                 const char     *title,
							 CslSample     **sample_p);
static void		arts_sample_cache_remove	(CslSample	*sample);


/* --- varables --- */
CslPcmDriverVTable _csl_arts_pcm_vtable = {
  arts_pcm_driver_init,
  arts_pcm_driver_shutdown,
  arts_pcm_stream_init,
  arts_pcm_stream_destroy,
  arts_pcm_read,
  arts_pcm_write,
  arts_pcm_update_status,
  arts_pcm_sync,
  arts_pcm_flush,
  arts_pcm_set_title,
  arts_pcm_set_stream_mode,
  arts_pcm_set_packet_mode,
  arts_pcm_set_channel_mapping,
};

CslSampleDriverVTable _csl_arts_sample_vtable = {
  arts_sample_driver_init,
  arts_sample_driver_shutdown,
  arts_sample_new,
  arts_sample_release,
  arts_sample_write,
  arts_sample_write_done,
  arts_sample_play,
  arts_sample_is_playing,
  arts_sample_stop,
  arts_sample_wait_done,
  arts_sample_cache_add,
  arts_sample_cache_find,
  arts_sample_cache_remove
};


/* --- functions --- */
static void
arts_pcm_driver_free (CslPcmDriver *pcm_driver)
{
  if (pcm_driver->n_streams > 0)
    csl_warning ("shutting down PCM driver while %d streams still alive",
		 pcm_driver->n_streams);
  
  if (pcm_driver->server)
    {
      /* FIXME: _arts_server_destroy (data->server); */
    }
  
  if (pcm_driver->adapter)
    {
      /* FIXME: _arts_csl_adapter_destroy (data->adapter); */
    }
  
  if (pcm_driver->dispatcher)
    _arts_mcop_dispatcher_destroy (pcm_driver->dispatcher);
  
  if (pcm_driver)
    csl_free (pcm_driver);
}

static CslErrorType
arts_pcm_driver_init (CslDriver *driver)
{
  static const char *pcm_mappings[] = {
    CSL_PCM_CHANNEL_CENTER,
    CSL_PCM_CHANNEL_FRONT_LEFT,
    CSL_PCM_CHANNEL_FRONT_RIGHT,
  };
  CslPcmDriver *pcm_driver = csl_new0 (CslPcmDriver, 1);
  char *csl_server, *csl_cookie, *tmp, *string, *file;
  CslErrorType error = CSL_ENONE;
  unsigned int i;
  
  csl_server = getenv ("ARTS_SERVER");	/* e.g. ARTS_SERVER=localhost:9877 */
  csl_cookie = getenv ("ARTS_COOKIE");	/* e.g. ARTS_COOKIE=$HOME/.mcop_cookie */
  
  /* setup md5 cookies */
  tmp = getenv ("HOME");
  tmp = tmp ? tmp : "/tmp";
  string = "/.MCOP-random-seed";
  i = strlen (tmp) + strlen (string);
  file = csl_malloc (i + 1);
  file[0] = 0;
  strcat (file, tmp);
  strcat (file, string);
  _arts_md5_auth_init_seed (file);
  csl_free (file);
  
  /* initialize dispatcher:
   * o connect to aRts
   * o authorize via cockie
   * o fetch GlobalComm object via SERVER_HELLO urls
   * o lookup method "get" on GlobalComm
   * o "get" Arts_SoundServer from GlobalComm
   * o lookup method CreateObject () on SoundServer
   */

  /* setup MCOP dispatcher */
  if (!error)
    {
      pcm_driver->dispatcher = _arts_mcop_dispatcher_new (csl_server ? csl_server : "localhost:9877", csl_cookie);
      if (!pcm_driver->dispatcher)
	error = CSL_ENODRIVER;
    }

  /* get a proxy for the Arts::SoundServer */
  if (!error)
    {
      pcm_driver->server = _arts_sound_server_from_reference (pcm_driver->dispatcher,
							      pcm_driver->dispatcher->arts_sound_server, TRUE);
      if (!pcm_driver->server)
	error = CSL_ENODRIVER;
    }
  
  /* create csl adapter on the server */
  if (!error)
    {
      ArtsMcopObjectReference *object;
      
      object = _arts_sound_server_create_object (pcm_driver->server, "CSL::Adapter");
      if (!object)
	error = CSL_ENODRIVER;
      else
	{
	  pcm_driver->adapter = _arts_csl_adapter_from_reference (pcm_driver->dispatcher, object, FALSE);
	  _arts_mcop_object_reference_free (object);
	  if (!pcm_driver->adapter)
	    error = CSL_ENODRIVER;
	}
    }
  
  if (!error)
    {
      /* initialize the driver's PCM portion */
      driver->pcm_vtable = &_csl_arts_pcm_vtable;
      driver->pcm_data = pcm_driver;
      driver->n_pcm_mappings = sizeof (pcm_mappings) / sizeof (pcm_mappings[0]);
      driver->pcm_mappings = pcm_mappings;
    }
  else
    arts_pcm_driver_free (pcm_driver);
  
  if (csl_debug (PCM))
    csl_message ("+ARTS-STREAM-INIT(%s): %s",
		 error == CSL_ENONE ? pcm_driver->dispatcher->arts_server_name : "",
		 error == CSL_ENONE ? "succeeded" : "failed");
  
  return error;
}

static void
arts_pcm_driver_shutdown (CslDriver *driver)
{
  csl_return_if_fail (driver != NULL);
  csl_return_if_fail (driver->pcm_data != NULL);
  
  arts_pcm_driver_free (driver->pcm_data);

  if (csl_debug (PCM))
    csl_message ("-ARTS-STREAM-SHUTDOWN()");
}

static CslErrorType
arts_pcm_stream_init (CslDriver       *driver,
		      const char      *role,
		      unsigned int     rate,
		      unsigned int     n_channels,
		      CslPcmFormatType format,
		      CslBool	      readable,
		      CslPcmStream   **stream_p)
{
  CslPcmDriver *pcm_driver = driver->pcm_data;
  ArtsPcmStream *stream;
  CslErrorType error;
  int stream_id;
  int direction = readable ? ARTS_CSL_DIR_INPUT : ARTS_CSL_DIR_OUTPUT;
  
  stream_id = _arts_csl_adapter_open (pcm_driver->adapter, role, direction);
  if (!stream_id)
    return CSL_EIO;	/* this only happens if the server failed badly somehow (i.e. crash) */
  _arts_csl_adapter_activate (pcm_driver->adapter, stream_id);
  
  stream = csl_new0 (ArtsPcmStream, 1);
  stream->stream.driver = driver;
  stream->stream.role = csl_strdup (role);
  stream->stream.title = NULL;
  stream->stream.blocking = TRUE;
  stream->stream.readable = readable;
  stream->stream.packet_mode = FALSE;
  stream->stream.stream_mode = !stream->stream.packet_mode;
  stream->stream.n_channels = n_channels;
  stream->stream.channel_mappings = NULL;
  stream->stream.format = format;
  stream->stream.rate = rate;
  stream->stream.writable = !readable;
  stream->stream.readable = readable;
  
  /* pick buffering defaults for Arts fragments (packet_mode = TRUE)
     stream->stream.packet.n_total_packets = 128;
     stream->stream.packet.packet_size = 1024;
     stream->stream.packet.n_packets_available = 0; 
     stream->stream.packet.packet_watermark = 1;
  */
  stream->stream.packet.n_total_packets = 0;
  stream->stream.packet.packet_size = 0;
  stream->stream.packet.n_packets_available = 0; 
  stream->stream.packet.packet_watermark = 0;
  
  /* setup buffering values based on default packet setup */
  stream->stream.buffer_size = 128*1024;
  stream->stream.n_bytes_available = 0; /* unused in _setup() */
  stream->stream.buffer_watermark = 32*1024;
  stream->id = stream_id;
  stream->last_buffer_level = 0;
  
  driver->pcm_data->n_streams++;
  error = pcm_device_arts_setup (stream);
  if (error)
    arts_pcm_stream_destroy (&stream->stream);
  else
    {
      /* _setup() didn't fill in buffer settings */
      arts_pcm_update_status (&stream->stream);
      *stream_p = &stream->stream;
      arts_pcm_set_title (&stream->stream, stream->stream.role);
    }
  
  return error;
}

static void
arts_pcm_stream_destroy (CslPcmStream *_stream)
{
  CslPcmDriver *pcm_driver = _stream->driver->pcm_data;
  ArtsPcmStream *stream = ARTS_PCM_STREAM (_stream);
  
  if (stream->id != 0)
    {
      _arts_csl_adapter_wait_buffer_level (pcm_driver->adapter, stream->id, 0);
      _arts_csl_adapter_close (pcm_driver->adapter, stream->id);
      pcm_driver->n_streams--;
    }
  
  csl_free (stream->stream.channel_mappings);
  csl_free (stream->stream.role);
  csl_free (stream->stream.title);
  csl_free (stream);
}

static int
arts_pcm_read (CslPcmStream    *_stream,
	       unsigned int     n_bytes,
	       char            *bytes)
{
  ArtsPcmStream *stream = ARTS_PCM_STREAM (_stream);
  CslPcmDriver *pcm_driver = stream->stream.driver->pcm_data;
  unsigned int l = 0;
  unsigned int rbytes = 0;
  
  /* FIXME: works blocking only 
   *
   * what to do on server crash? currently, we will return the number of bytes
   * successfully read (maybe 0), and give no further notice that something bad
   * happened
   */
  
  do
    {
      rbytes = MIN (stream->stream.buffer_size / 2, n_bytes);
      
      if (csl_debug (PCM))
	csl_message ("Arts-Read: wait for rbytes=%d", rbytes);
      rbytes = _arts_csl_adapter_wait_buffer_level (pcm_driver->adapter, stream->id, rbytes);
      rbytes = MIN (rbytes, n_bytes);

      rbytes = _arts_csl_adapter_read (pcm_driver->adapter, stream->id, rbytes, bytes);
      if (csl_debug (PCM))
	csl_message ("Arts-Read: read rbytes=%d", rbytes);
      
      bytes = ((char *)bytes) + rbytes;
      n_bytes -= rbytes;
      l += rbytes;
    }
  while (stream->stream.blocking && n_bytes && rbytes);
  return l;
}

/* FIXME: BLOCK_SIZE would ideally be a multiple of TCP-buffer-size, need to figure
 * system-specific best blocksize via configure.in
 */
#define	BLOCK_SIZE	(4096)

static int
arts_pcm_write (CslPcmStream *_stream,
		unsigned int  n_bytes,
		char         *bytes)
{
  ArtsPcmStream *stream = ARTS_PCM_STREAM (_stream);
  CslPcmDriver *pcm_driver = stream->stream.driver->pcm_data;
  unsigned int bsize, l = 0;

  if (!stream->stream.blocking)
    {
      csl_warning ("FIXME: need non-blocking write() impl for Arts driver");
      return CSL_ENOIMPL;
    }

  csl_assert (BLOCK_SIZE <= stream->stream.buffer_size); // FIXME: assert this somewhere else
  bsize = MIN (BLOCK_SIZE, n_bytes);
  while (bsize)
    {
      if (bsize > stream->stream.buffer_size - stream->last_buffer_level)
	stream->last_buffer_level = _arts_csl_adapter_wait_buffer_level (pcm_driver->adapter, stream->id,
									 stream->stream.buffer_size - bsize);
      csl_assert (stream->last_buffer_level <= stream->stream.buffer_size);

      _arts_csl_adapter_write (pcm_driver->adapter, stream->id, bytes, bsize);
      stream->last_buffer_level += bsize;
      bytes += bsize;
      n_bytes -= bsize;
      l += bsize;
      bsize = MIN (BLOCK_SIZE, n_bytes);
    }
  
  return l;
}

static CslErrorType
arts_pcm_update_status (CslPcmStream *_stream)
{
  CslPcmDriver *pcm_driver = _stream->driver->pcm_data;
  ArtsPcmStream *stream = ARTS_PCM_STREAM (_stream);
  int bytes;
  
  ArtsCslStreamStatus *status = _arts_csl_adapter_status (pcm_driver->adapter, stream->id);

  if (status == NULL) /* server crash? */
    return CSL_EIO;

  if (stream->stream.readable)
    bytes = status->stream_buffer_used;
  else
    bytes = stream->stream.buffer_size - status->stream_buffer_used;

  stream->stream.n_bytes_available = CLAMP (bytes, 0, stream->stream.buffer_size);
  _arts_csl_stream_status_free (status);

  if (csl_debug (PCM))
    csl_message ("Arts-STATUS: buffer level=%d/%d bytes",
		 stream->stream.n_bytes_available,
		 stream->stream.buffer_size);
  
  return CSL_ENONE;
}

static CslErrorType
arts_pcm_flush (CslPcmStream *_stream)
{
  return CSL_ENODRIVER;
#if 0
  ArtsPcmStream *stream = ARTS_PCM_STREAM (_stream);
  
  /* crarts fingers that format, fragment settings etc. remain
   * untouched acrarts Arts driver RESET implementations
   */
  (void) ioctl (stream->fd, SNDCTL_DSP_RESET);
  
  return CSL_ENONE;
#endif
}

static CslErrorType
arts_pcm_sync (CslPcmStream *_stream)
{
  CslPcmDriver *pcm_driver = _stream->driver->pcm_data;
  ArtsPcmStream *stream = ARTS_PCM_STREAM (_stream);
  
  _arts_csl_adapter_wait_buffer_level (pcm_driver->adapter, stream->id, 0);
  /* FIXME: wait until /PHYSICAL/ samples are played, not until our buffer is empty */
  
  return CSL_ENONE;
}

static CslErrorType
arts_pcm_set_title (CslPcmStream *_stream,
		    const char   *title)
{
  ArtsPcmStream *stream = ARTS_PCM_STREAM (_stream);
  CslPcmDriver *pcm_driver = stream->stream.driver->pcm_data;
  
  csl_free (stream->stream.title);
  stream->stream.title = csl_strdup (title);
  _arts_csl_adapter_set_title (pcm_driver->adapter, stream->id, stream->stream.title);

  return CSL_ENONE;
}

static CslErrorType
arts_pcm_set_stream_mode (CslPcmStream   *stream,
			  unsigned int    buffer_size,
			  unsigned int    byte_watermark)
{
  /* FIXME: implement watermark */
  csl_warning ("FIXME: watermark not implemented for aRts driver");
  return CSL_ECAPSUPPORT;
}

static CslErrorType
arts_pcm_set_packet_mode	(CslPcmStream   *_stream,
				 unsigned int	 n_packets,
				 unsigned int	 packet_size,
				 unsigned int	 packet_watermark)
{
  csl_warning ("FIXME: packet mode not implemented for aRts driver");
  return CSL_ENONE;
}

static CslErrorType
arts_pcm_set_channel_mapping (CslPcmStream   *stream,
			      unsigned int    channel,
			      const char     *mapping)
{
  /* FIXME: need channel mapping implemetation for CSL::Adapter in Arts? */
  return CSL_ECAPSUPPORT;
}


static CslErrorType
pcm_device_arts_setup (ArtsPcmStream *stream)
{
  CslPcmDriver *pcm_driver = stream->stream.driver->pcm_data;
  
  ArtsCslStreamParams params = {
    stream->stream.rate,
    stream->stream.n_channels,
    stream->stream.format
  };
  
  if (csl_debug (PCM))
    csl_message ("ARTS-SETUP: format=%x channels=%d",
		 stream->stream.format, stream->stream.n_channels);
  
  if (!_arts_csl_adapter_set_params (pcm_driver->adapter, stream->id, &params))
    {
      return CSL_ECAPSUPPORT;
    }
  
  
  return CSL_ENONE;
#if 0
  unsigned int arts_format;
  int fd = stream->fd;
  long d_long;
  int d_int;
  
  /* control blocking */
  d_long = fcntl (fd, F_GETFL, 0);
  if (stream->stream.blocking)
    d_long &= ~O_NONBLOCK;
  else
    d_long |= O_NONBLOCK;
  if (fcntl (fd, F_SETFL, d_long))
    return CSL_EIO;
  
  /* sample format */
  arts_format = arts_format_translate (stream->stream.format);
  if (arts_format == FMT_ERROR)
    return CSL_EFMTINVAL;
  d_int = arts_format;
  if (ioctl (fd, SNDCTL_DSP_GETFMTS, &d_int) < 0)
    return CSL_EGETCAPS;
  if ((d_int & arts_format) != arts_format)
    return CSL_ECAPSUPPORT;
  d_int = arts_format;
  if (ioctl (fd, SNDCTL_DSP_SETFMT, &d_int) < 0 ||
      d_int != arts_format)
    return CSL_ESETCAPS;
  
  /* number of channels (STEREO || MONO) */
  if (stream->stream.n_channels < 1 || stream->stream.n_channels > 2)
    return CSL_ECAPSUPPORT;
  d_int = stream->stream.n_channels - 1;
  if (ioctl (fd, SNDCTL_DSP_STEREO, &d_int) < 0)
    return CSL_ESETCAPS;
  stream->stream.n_channels = d_int + 1;
  
  /* channel mappings */
  if (stream->stream.n_channels == 1)
    {
      stream->stream.channel_mappings = csl_new (char*, 1);
      stream->stream.channel_mappings[0] = CSL_PCM_CHANNEL_CENTER;
    }
  else /* stereo */
    {
      stream->stream.channel_mappings = csl_new (char*, 2);
      stream->stream.channel_mappings[0] = CSL_PCM_CHANNEL_FRONT_LEFT;
      stream->stream.channel_mappings[1] = CSL_PCM_CHANNEL_FRONT_RIGHT;
    }
  
  /* frequency or sample rate */
  d_int = stream->stream.rate;
  if (ioctl (fd, SNDCTL_DSP_SPEED, &d_int) < 0)
    return CSL_ESETCAPS;
  stream->stream.rate = d_int;
  
  /* buffer settings */
  if (stream->stream.stream_mode)
    {
      return CSL_ECAPSUPPORT; // FIXME
    }
  else
    {
      unsigned int fragment_size, n_fragments;
      
      /* Note: fragment = n_fragments << 16;
       *       fragment |= g_bit_storage (fragment_size - 1);
       */
      fragment_size = CLAMP (stream->stream.packet.packet_size, 128, 32768);
      n_fragments = CLAMP (stream->stream.packet.n_total_packets, 2, 128);
      d_int = (n_fragments << 16) | csl_bit_storage (fragment_size - 1);
      if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &d_int) < 0)
	return CSL_ESETCAPS;
    }
  
  /* check buffer settings retrieval */
  if (stream->stream.writable)
    {
      audio_buf_info info = { 0, };
      
      if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &info) < 0)
	return CSL_EGETCAPS;
    }
  if (stream->stream.readable)
    {
      audio_buf_info info = { 0, };
      
      if (ioctl (fd, SNDCTL_DSP_GETISPACE, &info) < 0)
	return CSL_EGETCAPS;
    }
  
  if (csl_debug (PCM))
    csl_message ("Arts-SETUP(%s): w=%d r=%d n_channels=%d rate=%d bufsz=%d bufavail=%d",
		 stream->device_name,
		 stream->stream.writable,
		 stream->stream.readable,
		 stream->stream.n_channels,
		 stream->stream.rate,
		 stream->stream.buffer_size,
		 stream->stream.n_bytes_available);
  
  return CSL_ENONE;
#endif
}

CslErrorType
arts_sample_driver_init	(CslDriver *driver)
{
  /* FIXME: this assumes that the pcm driver will always be there, can I assume this here? */
  if (driver->pcm_data)
    {
      /* initialize the driver's sample portion */
      driver->sample_vtable = &_csl_arts_sample_vtable;
      driver->sample_data = csl_new0 (CslSampleDriver, 1);
      return CSL_ENONE;
    }
  else
    return CSL_ENODRIVER;
}

void
arts_sample_driver_shutdown (CslDriver *driver)
{
  csl_return_if_fail (driver != NULL);
  csl_return_if_fail (driver->sample_data != NULL);

  if (driver->sample_data->n_samples > 0)
    csl_warning ("shutting down sample driver while %d samples still alive",
		 driver->sample_data->n_samples);

  csl_free (driver->sample_data);
}

CslErrorType
arts_sample_new	(CslDriver	*driver,
		 const char     *name,
		 const char     *role,
		 const char     *title,
		 CslSample     **sample_p)
{
  CslPcmDriver *pcm_driver = driver->pcm_data;
  int sample_id;
  ArtsSample *sample;
 
  sample_id = _arts_csl_adapter_sample_new (pcm_driver->adapter, name, role, title);
  if (!sample_id)   /* server crash */
    {
      *sample_p = NULL;
      return CSL_EIO;
    }
  
  sample = csl_new0 (ArtsSample, 1);
  sample->sample.driver = driver;
  sample->id = sample_id;
  *sample_p = &sample->sample;
 
  driver->sample_data->n_samples++;
  return CSL_ENONE;
}

void
arts_sample_release (CslSample *_sample)
{
  ArtsSample *sample = ARTS_SAMPLE (_sample);
  CslPcmDriver *pcm_driver = sample->sample.driver->pcm_data;

  _arts_csl_adapter_sample_release (pcm_driver->adapter, sample->id);
  sample->sample.driver->sample_data->n_samples--;

  csl_free (_sample);
}

int
arts_sample_write (CslSample	  *_sample,
		   unsigned int    n_bytes,
		   void           *bytes)
{
  ArtsSample *sample = ARTS_SAMPLE (_sample);
  CslPcmDriver *pcm_driver = sample->sample.driver->pcm_data;

  _arts_csl_adapter_sample_write (pcm_driver->adapter, sample->id, bytes, n_bytes);
  return n_bytes; /* FIXME: server crashes? */
}

CslErrorType
arts_sample_write_done (CslSample *_sample)
{
  ArtsSample *sample = ARTS_SAMPLE (_sample);
  CslPcmDriver *pcm_driver = sample->sample.driver->pcm_data;

  _arts_csl_adapter_sample_write_done (pcm_driver->adapter, sample->id);
  return CSL_ENONE; /* FIXME: server crashes? */
}

CslErrorType
arts_sample_play (CslSample *_sample)
{
  ArtsSample *sample = ARTS_SAMPLE (_sample);
  CslPcmDriver *pcm_driver = sample->sample.driver->pcm_data;
  CslBool result;

  result = _arts_csl_adapter_sample_play (pcm_driver->adapter, sample->id);
  return result ? CSL_ENONE : CSL_EIO; /* FIXME: is EIO right here? */
}

CslBool
arts_sample_is_playing (CslSample *_sample)
{
  ArtsSample *sample = ARTS_SAMPLE (_sample);
  CslPcmDriver *pcm_driver = sample->sample.driver->pcm_data;

  return _arts_csl_adapter_sample_is_playing (pcm_driver->adapter, sample->id);
}
  
CslErrorType
arts_sample_stop (CslSample *_sample)
{
  ArtsSample *sample = ARTS_SAMPLE (_sample);
  CslPcmDriver *pcm_driver = sample->sample.driver->pcm_data;

  _arts_csl_adapter_sample_stop (pcm_driver->adapter, sample->id);
  return CSL_ENONE; /* FIXME: server crashes */
}
  
CslErrorType
arts_sample_wait_done (CslSample *_sample)
{
  ArtsSample *sample = ARTS_SAMPLE (_sample);
  CslPcmDriver *pcm_driver = sample->sample.driver->pcm_data;

  _arts_csl_adapter_sample_wait_done (pcm_driver->adapter, sample->id);
  return CSL_ENONE; /* FIXME: server crashes */
}

CslErrorType
arts_sample_cache_add (CslSample *_sample)
{
  ArtsSample *sample = ARTS_SAMPLE (_sample);
  CslPcmDriver *pcm_driver = sample->sample.driver->pcm_data;

  _arts_csl_adapter_sample_cache_add (pcm_driver->adapter, sample->id);

  return CSL_ENONE;
}

CslErrorType
arts_sample_cache_find (CslDriver        *driver,
			const char       *name,
			const char       *role,
			const char       *title,
			CslSample       **sample_p)
{
  CslPcmDriver *pcm_driver = driver->pcm_data;
  ArtsSample *sample;
  int sample_id;
  
  sample_id = _arts_csl_adapter_sample_cache_find (pcm_driver->adapter, name, role, title);
  if (!sample_id)
    {
      *sample_p = NULL;	/* server crash OR sample not found in cache */
      return CSL_EIO;
    }

  sample = csl_new0 (ArtsSample, 1);
  sample->sample.driver = driver;
  sample->id = sample_id;
  
  driver->sample_data->n_samples++;
  *sample_p = &sample->sample;

  return CSL_ENONE;
}

void
arts_sample_cache_remove (CslSample *_sample)
{
  ArtsSample *sample = ARTS_SAMPLE (_sample);
  CslPcmDriver *pcm_driver = sample->sample.driver->pcm_data;

  _arts_csl_adapter_sample_cache_remove (pcm_driver->adapter, sample->id);
}

/* vim:set ts=8 sw=2 sts=2: */
