/*
    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 __GDAM_MODEL_H_
#define __GDAM_MODEL_H_


typedef struct _GdamModelClass GdamModelClass;
typedef struct _GdamModel GdamModel;
typedef struct _GdamPlayInfo GdamPlayInfo;

#include "gdambeatinfo.h"
#include "gdamxmlinfo.h"

#include <gtk/gtkwidget.h>
#include <gdam/client/gdamchannel.h>
#include <gnome-xml/parser.h>
#include "gdaminfomanager.h"
#include "gdamrequestbuilder.h"

GtkType gdam_model_get_type();
#define GDAM_TYPE_MODEL              (gdam_model_get_type ())
#define GDAM_MODEL(obj)              (GTK_CHECK_CAST ((obj), GDAM_TYPE_MODEL, GdamModel))
#define GDAM_MODEL_CLASS(klass)      (GTK_CHECK_CLASS_CAST ((klass), GDAM_TYPE_MODEL, GdamModelClass))
#define GDAM_IS_MODEL(obj)           (GTK_CHECK_TYPE ((obj), GDAM_TYPE_MODEL))
#define GDAM_IS_MODEL_CLASS(klass)   (GTK_CHECK_CLASS_TYPE ((klass), GDAM_TYPE_MODEL))

#define GDAM_BUTTON23_MASK (enum GdkModifierType)(GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)

#define GDAM_MODEL_GET_CHANNEL(model)	(GDAM_MODEL (model)->channel)

/*
 *               GdamModel
 *
 * The base class for the structure which describes a server-side
 * sound source (or a few sources which work together).
 *
 * It contains a `channel' which identifies the server,
 * and a `source_id' which is the identifying number of
 * the source on the server.
 *
 * It contains a vector of argument sets which are pending to
 * the server -- this is a convenient mechanism for congestion
 * avoidance when setting things on the server.
 */


/* TODO: move this structure to producer.h?? */
/*  This structure contains information about how a producer should start 
 *  playing.  A producer may ignore all of this information.  Fields left
 *  zero are ignored.  be sure to create with g_new0();
 */
struct _GdamPlayInfo {
	gdouble  		position;
	gdouble  		length;
	guint			lead_id;
	GdamBeatInfo*		lead_beat_info;
	gboolean		is_synchronized;
};

struct _GdamModelClass {
	GtkObjectClass		object_class;

	/* Specify the frequency and position of the beats.
	 * For example an mp3-model does this when
	 * a song is loaded. */
	gboolean              (*set_beat_info)  (GdamModel* model,
	                                         const GdamBeatInfo* new_info);

	/* Sources which use buffers must relinquish them when
	 * this signal is received. */
	void                  (*detach_from_buffer)
	                                        (GdamModel* model,
						 guint          buffer_id);

	gdouble               (*get_length)     (GdamModel*     model);

	/*
	 * Takes a GdamBeatInfo structure and attempts to match the
	 * songs output to have that tempo.
	 *
	 * In a turntable, this will cause the output to match by
	 * resampling and nudging.
	 */
	gboolean              (*match_beat_info)(GdamModel*     model,
	                                         const GdamBeatInfo* to_match);

	/* Seek a source by an even number of beats.   The source must
	 * know its tempo, and have seeking ability for this to work.
	 */
	gboolean              (*nudge_by_beat)  (GdamModel*     model,
	                                         gdouble        num_beats);

	/* Notify us that our child wants to create a source. */
	void                  (*new_request)    (GdamModel*     model,
						 GdamModel*     child,
						 GdamPlayInfo*  play_info,
						 int            local_src_id,
						 GdamRequestBuilder*);

	void                  (*new_source_id)  (GdamModel*     model,
	                                         int            source_id);

	guint			num_arg_sets;

	GdamModel*            (*get_child)      (GdamModel*     model,
	                                         const char*    child_name);

	/* Serialization.
	 * Unserialization.
	 *
	 * Most of the work is done by the argument system,
	 * at least if you're lucky.  If not, you'll have to
	 * override these and switch on subtags.
	 *
	 * You'll have to at least do this for your children,
	 * if GDAM_XML_CHILDREN is set.
	 */
	void                  (*add_xml)            (GdamModel*     model,
	                                             xmlNode*       parent,
						     GdamXmlInfo*   xml_info);

	/* Parses a single nonargument xml node. */
	void                  (*parse_xml)          (GdamModel*     model,
	                                             xmlNode*       xml,
						     GdamXmlInfo*   xml_info);
};

struct _GdamModel {
	GtkObject		object;

	GdamModel*              parent;

	/* The connection to the server. */
	GdamChannel*		channel;

	/* The identifying number of our source on the server. */
	int                   source_id;

	/* The current beat information. */
	GdamBeatInfo		beat_info;

	/* The list of GdamView's we are connected to.
	 * TODO: rename this. */
	GSList*			skin_list;

	/* Private. */
	guint8*                 making_arg_requests;
	gpointer*               pending_arg_requests;

	guint			anchored : 1;
};

typedef void (*GdamModelNotify)(GdamModel*        model,
                                gpointer          data);



gboolean       gdam_model_nudge_by_beat         (GdamModel*       model,
	                                         gdouble          num_beats);
void           gdam_model_set_source_id         (GdamModel*       model,
				                 int              source_id);
void           gdam_model_set_beat_info         (GdamModel*       model,
				                 const GdamBeatInfo* info);
gdouble        gdam_model_get_length            (GdamModel*       model);

/* Set server-side arguments with congestion avoidance. */
void           gdam_model_set_args              (GdamModel*       model, 
						 unsigned         arg_index,
                                                 int              num_args,
			                         GdamArg*         args);
void           gdam_model_set_args_full         (GdamModel*       model, 
			                         int              source_id,
			                         unsigned         arg_set,
                                                 int              num_args,
			                         GdamArg*         args);

void           gdam_model_call_when_source_ready(GdamModel*       model,
				                 GdamModelNotify  notify_func,
				                 GDestroyNotify   destroy,
				                 gpointer         data);

/* Take a list like ``component::child::child'' and traverse
 * down calling ::get_child to new descend each level.
 *
 * Return the resulting child or NULL if a mismatch or error occurs. */
GdamModel*     gdam_model_traverse_child_tree   (GdamModel*       start,
                                                 const char*      tree_path);

/* Called by the child to have the parent package the request... */
void gdam_model_new_child_request               (GdamModel*       parent,
                                                 GdamModel*       child,
				                 GdamPlayInfo*    play_info,
				                 int              local_src_id,
				                 GdamRequestBuilder* builder);

/*                   gdam_model_got_subsource
 *                   gdam_model_didnt_get_subsource
 *
 * These should be passed as `success' and `failure' to
 * gdam_request_create_source, after gtk_object_ref'ing the
 * model.
 *
             gtk_object_ref(GTK_OBJECT(model));
             request = gdam_request_create_source
 			(type_name,
			 gdam_model_got_subsource,
			 gdam_model_didnt_get_subsource,
			 model,
			 -arguments-);
 */

void            gdam_model_got_subsource          (int            source_id,
                                                   gpointer       model);
void            gdam_model_didnt_get_subsource    (gpointer       model);


/*
 *                            gdam_model_from_xml
 *                             gdam_model_to_xml
 *
 * Serialize/deserialize model as xml.
 */
GdamModel*  	gdam_model_from_xml               (xmlNode*       node,
				                   GdamChannel*   channel,
						   GdamXmlInfo*   info);
xmlNode*        gdam_model_to_xml                 (GdamModel*     model,
                                                   GdamXmlInfo*   info);


/* gdam_arg_clone_object + sets the GdamChannels up properly... */
GdamModel*      gdam_model_clone                  (GdamModel*     model);

/* this can be used to quickly fabricate a model from a string;
 * GdamBuilder{component=GdamTurntable{producer=GdamProducerMp3}}{filter#0=GdamGain}
 *
 * syntax for arguments:
 *       GdamProducerMp3(filename=/mp3/???)
 * parentheses-grouped and brace-grouped may be mixed,
 * with no intervening punctuation.
 */
GdamModel*      gdam_model_from_spec              (const char*    spec,
                                                   GdamChannel*   channel);

#endif
