/*
    GDAM - Geoff & Dave's Audio Mixer
    Copyright (C) 1999    Dave Benson, Geoff Matters.

    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

    Contact:
        daveb@ffem.org <Dave Benson>
        geoff@ugcs.caltech.edu <Geoff Matters>
*/
#ifndef __GDAS_SOURCE_H__
#define __GDAS_SOURCE_H__

typedef struct _GdasSource GdasSource;
typedef struct _GdasSourceClass GdasSourceClass;
typedef struct _GdasSourceControl GdasSourceControl;
typedef struct _GdamLatencyInfo GdamLatencyInfo;

#include <gtk/gtkobject.h>
#include "gdamsink.h"
#include "gdamserverobject.h"
#include <gdam/common/gdambuffer.h>
#include <gdam/common/gdamsample.h>
#include <gdam/common/gdamarg.h>


#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */


GtkType gdas_source_get_type();
#define GDAS_TYPE_SOURCE              (gdas_source_get_type ())
#define GDAS_SOURCE(obj)              (GTK_CHECK_CAST ((obj), GDAS_TYPE_SOURCE, GdasSource))
#define GDAS_SOURCE_CLASS(klass)      (GTK_CHECK_CLASS_CAST ((klass), GDAS_TYPE_SOURCE, GdasSourceClass))
#define GDAS_IS_SOURCE(obj)           (GTK_CHECK_TYPE ((obj), GDAS_TYPE_SOURCE))
#define GDAS_IS_SOURCE_CLASS(klass)   (GTK_CHECK_CLASS_TYPE ((klass), GDAS_TYPE_SOURCE))

guint gdas_source_get_ended_event();
#define GDAS_SOURCE_EVENT_ENDED		(gdas_source_get_ended_event())

guint gdas_source_event_timer();
#define GDAS_SOURCE_EVENT_TIMER         (gdas_source_event_timer())


/*
 *                       GdasSource
 *
 * The base class for all server-side sources.
 *
 * The principle function is ``read'' which should
 * induce the source to produce samples.
 *
 * This function is complimented by ``is_ready_to_read''
 * which should determine whether the source can be 
 * read from without blocking.
 *
 * If you are writing a new source, you should try
 * deriving from an existing source:
 *
 *      GdasFilter            base class for source which produce from
 *                                sound based on the sound of another source.
 *      GdasMix               sometimes a useful source for collections.
 *
 * Additional interface which should be implemented by
 * derivers:
 *
 *      get_latency_info      latency induced by this source [for synching]
 *      get_position          return the source's "position", only used
 *                            for client convenience -- sources should
 *                            track their clients directly.
 * TODO: babble on...
 *      seek
 *      detach_child
 *      get_subsources
 *      
 */

/*
 * Latency information:
 */
struct _GdamLatencyInfo
{
  int			latency;
  GdamFixed		resample;
};

typedef gint (*GdasSourceControlFunc)(GdasSource     *source,
				      gpointer        control_data);


struct _GdasSource
{
  GdamServerObject        object;
  
  GdasSource             *parent;
  GdamSink               *device;

  GdasSourceControl      *control_list;

  /* The ended flag indicates the source is done outputing data;
   * we depend on a signal being emitted when this flag
   * is set, so use ``gdas_source_has_ended'' to set it.
   *
   * XXX: there should be a definite policy on clearing this
   *      flag.  Right now, it is okay basically to clear the flag.*/
  unsigned                ended : 1,
			  paused : 1;
};

struct _GdasSourceClass
{
  GdamServerObjectClass parent_class;
  /* deprecated: interpret_command */
  GdamBuffer       *(*interpret_command) (GdasSource      *source,
					  GdamBuffer      *command,
					  gboolean        *was_success);

  void              (*get_latency_info)  (GdasSource      *source,
					  GdasSource      *subsource,
					  GdamLatencyInfo *latency);

  void              (*done_constructing) (GdasSource      *source);

  /* If this is NULL, then the source is always ready-to-read. */
  gboolean          (*is_ready_to_read)  (GdasSource      *source);

  /* Get the position... only used for user queries; do not
   * rely on it internally!!! */
  GdamTime          (*get_position)      (GdasSource      *source);

  /* Get the subsources of a source.
   * The caller should expect an array in *children
   * which must be freed by the caller. */
  void              (*get_subsources)    (GdasSource      *source,
					  int             *num_children,
					  GdasSource    ***children);

  /* Return values:
   *      positive: the number of bytes written into buffer,
   *      0 normal end of stream,
   *      -1 error end of stream.
   */
  int                (*read)             (GdasSource      *input,
					  GdamSample      *samples,
					  int              max_read);
  
#if 0
  int                (*read_full)        (GdasSource      *input,
					  GdamSampleInfo  *sample_format,
					  void            *sample_data,
					  int              max_read);
#endif

  /* Seek a sound source. */
  gboolean           (*seek)             (GdasSource      *input,
					  GdamTime         offset,
					  int              whence);

  /* Inform the source that some data was lost. */
  void               (*miss_data)        (GdasSource      *input,
					  int              samples_lost);

  /* For container sources, remove that child. */
  gboolean           (*detach_child)     (GdasSource      *parent, 
					  GdasSource      *child);

  /* Called by gdas_source_read (or whoever else uses the
   * read virtual) to indicate the source has EOF'd. */
  void               (*ended)            (GdasSource      *source);
};

#ifdef __cplusplus
}
#endif /* __cplusplus */


GdasSource        *gdas_source_new_generic_v(const char *type,
                                             int         num_args,
                                             GdamArg    *args);
GdasSource        *gdas_source_new_generic  (const char *type,
                                             int         num_args,
                                             ...);
void               gdas_source_set_gdam_arg (GdasSource *source, 
                                             GdamArg    *arg);
void               gdas_source_get_gdam_arg (GdasSource *source, 
                                             GdamArg    *arg);


gboolean           gdas_source_detach_child (GdasSource *parent,
                                             GdasSource *child);

/* Get a source class from a well-known (by the gdam protocol)
 * type number.
 * Used by `gdamdispatch' to get at our idea of things from
 * the well-known protocol.
 */
GtkType            gdas_source_type_to_gtk_type(int gdam_protocol_type);

/* Detach it from whatever source it may be feeding into.
 *
 * FIXME (and all dependant code): increments the source's ref count... */
void               gdas_source_orphan          (GdasSource      *source);

/* Compute how far to seek a source to sync it. */
gint               gdas_source_get_sync_seek   (GdasSource      *mobile,
                                                GdasSource      *fixed,

                                                /* We cheat and sneak this 
                                                 * value in b32... */
                                                GdamTime         mobile_period,
                                                /* Just in samples... */
                                                GdamTime         mobile_offset,

                                                /* We cheat again... */
                                                GdamTime         fixed_period,
                                                /* Just in samples... */
                                                GdamTime         fixed_offset,
                                                
                                                /* Something to use for
                                                 * latency if we aren't added
                                                 * yet... */
                                                GdasSource      *mobile_lat);


gboolean           gdas_source_is_ancestor      (GdasSource     *child,
                                                 GdasSource*
                                                           potential_ancestor);
gboolean           gdas_source_is_ready_to_read (GdasSource     *source);
int                gdas_source_read             (GdasSource     *input,
                                                 GdamSample     *samples,
                                                 int             max_read);
gboolean           gdas_source_seek             (GdasSource     *source,
                                                 GdamTime        offset,
                                                 int             whence);
void               gdas_source_miss_data        (GdasSource     *input,
                                                 int             samples_lost);
GdamBuffer        *gdas_source_interpret_command(GdasSource     *source,
                                                 GdamBuffer     *command,
                                                 gboolean       *was_success);
GdamTime           gdas_source_get_position     (GdasSource     *source);
void               gdas_source_find_total_latency(GdasSource    *source,
                                                 GdamLatencyInfo *total);

/* The caller must free the array returned in *subs_out. */
void               gdas_source_get_subsources   (GdasSource     *source, 
                                                 int            *num_subs_out,
                                                 GdasSource   ***subs_out);

/* Generic control facilities. */
GdasSourceControl *gdas_source_add_control      (GdasSource     *source,
						 guint           next_call,
						 GdasSourceControlFunc control,
						 gpointer        control_data,
						 GDestroyNotify  destroy);
void               gdas_source_remove_control   (GdasSource     *source,
						 GdasSourceControl *control);

/* Functions to globally freeze and unfreeze done_constructing callbacks.
 * This is used inside package request handlers to prevent multiple
 * tweak from creating too many mpg123s. */
void gdas_source_defer_done_constructing();
void gdas_source_undefer_done_constructing();

/* Derivers can use this when returning a non-zero value
 * to indicate that the source has ended (will cause the ended signal). */
void gdas_source_has_ended(GdasSource*);

/* declare this function here -- for .h file craziness resolution ...
 * Note that this function gtk_object_ref's its return value. */
GdasSource          *gdam_connection_get_source         (GdamConnection*,
                                                         int        local_id);


#endif
