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

typedef struct _GdamArg GdamArg;
#define GDAM_ARG_MAX_ARGS 10000

#include <glib.h>
#include <gtk/gtkobject.h>
#include "gdambuffer.h"

typedef void        (*GdamArgSetFunc)         (GtkObject      *object,
                                               GdamArg        *argument);
typedef void        (*GdamArgGetFunc)         (GtkObject      *object,
                                               GdamArg        *argument);
typedef void        (*GdamArgGenericFunc)     (GtkObject      *object,
                                               int             num_args_in,
                                               GdamArg        *arguments_in,
                                               int            *num_args_out,
                                               GdamArg       **arguments_out);
typedef void        (*GdamArgGenericClassFunc)(GtkObjectClass *object_class,
                                               int             num_args_in,
                                               GdamArg        *arguments_in,
                                               int            *num_args_out,
                                               GdamArg       **arguments_out);
typedef void        (*GdamArgFunc)            (gpointer        user_data,
                                               GdamArg        *arg,
                                               GtkObject      *object);
typedef void        (*GdamArgSetChildFunc)    (GtkObject      *parent,
                                               const char     *child_name,
                                               int             child_index,
                                               GtkObject      *child);
typedef GtkObject  *(*GdamArgGetChildFunc)    (GtkObject      *parent,
                                               const char     *child_name,
                                               int             child_index);
typedef void        (*GdamArgChildFunc)       (GtkObject      *parent,
                                               GtkObject      *child,
                                               const char     *child_name,
                                               gpointer        user_data);
typedef void        (*GdamArgChildForeachFunc)(GtkObject      *object,
                                               gpointer        user_data);
typedef gboolean    (*GdamArgValidator)       (GtkObject      *object,
                                               GdamArg        *arg,
                                               gpointer        validation_data);
typedef struct _GdamValueDoubleArray GdamValueDoubleArray;
typedef struct _GdamValueStringArray GdamValueStringArray;
typedef struct _GdamValueIntArray GdamValueIntArray;
typedef struct _GdamValueInt64Array GdamValueInt64Array;

#define GDAM_ARG_FUNC(func_ptr)		((GdamArgFunc) (func_ptr))

typedef enum {
  GDAM_ARG_TYPE_INT = 14000,  /* a weird value so we can notice errors */
  GDAM_ARG_TYPE_INT64,
  GDAM_ARG_TYPE_DOUBLE,
  GDAM_ARG_TYPE_STRING,

  GDAM_ARG_TYPE_INT_ARRAY,
  GDAM_ARG_TYPE_INT64_ARRAY,
  GDAM_ARG_TYPE_DOUBLE_ARRAY,
  GDAM_ARG_TYPE_STRING_ARRAY
} GdamArgType;


typedef enum _GdamArgOptions {
  GDAM_ARG_VITAL         =  (1<<0),

  /* Means that getting the argument can cause something
   * to happen.  Must be explicitly queried -- even get_all
   * doesn't do this. */
  GDAM_ARG_TRIGGER       =  (1<<1),

  /* the arg shouldn't be used anymore */
  GDAM_ARG_DEPRECATED    =  (1<<2)
} GdamArgOptions;

struct _GdamValueDoubleArray
{
  gint      length;
  gdouble  *array;
};
struct _GdamValueStringArray
{
  gint      length;
  char    **array;
};
struct _GdamValueIntArray
{
  gint      length;
  int      *array;
};
struct _GdamValueInt64Array
{
  gint      length;
  gint64   *array;
};


struct _GdamArg 
{
  char                  *name;
  GdamArgType            type;
  union {
    gint                 int_data;
    gint64               int64_data;
    gdouble              double_data;
    gchar               *string_data;
    GdamValueInt64Array  int64_array;
    GdamValueIntArray    int_array;
    GdamValueStringArray string_array;
    GdamValueDoubleArray double_array;
  } d;
};

void          gdam_arg_init();

/* Parse an array of arguments out of a GdamBuffer. */
GdamArg      *gdam_arg_parse_from_buffer(GdamBuffer     *buffer,
                                         int             num_args);

/* Parse an array of arguments out of a va_list (for convenience when
 * you want to manufacture arguments inline. */
GdamArg      *gdam_arg_parse_from_vararg(int             num_args,
                                         va_list        *args);

/* Really just useful when you need to construct from arguments
 * quick-and-dirty.  */
GdamArg      *gdam_arg_new_from_valist  (int             num_args,
                                         ...);



/* Write an array of arguments into a buffer in a way that
 * `gdam_arg_parse_from_buffer' can retrieve them.
 * Note that the number of arguments is not stored-- you must pass
 * the same number to gdam_arg_parse_from_buffer. */
void          gdam_arg_write_to_buffer  (GdamBuffer     *buffer,
                                         int             num_args,
                                         const GdamArg  *args);

/* Make a human-readable string of the value of the argument. */
char         *gdam_arg_val_as_string    (GdamArg        *arg);
char         *gdam_arg_array_as_string  (int             num_args,
                                         GdamArg        *arg);

/* Returns the number of characters parsed as a value.
 * Caller should treated rv <= 0 as errors. */
int           gdam_arg_parse_val_from_string 
                                        (GdamArg        *arg,
                                         const char     *text);

enum {
	GDAM_ARG_CLONE_SHALLOW = 0,
	GDAM_ARG_CLONE_DEEP    = (1<<0)
};

/* Clone an object, argument by argument. */
GtkObject    *gdam_arg_clone_object     (GtkObject      *object,
                                         int             clone_flags);

void          gdam_arg_child_forall     (GtkObject      *object,
                                         GtkType         type,
					 GdamArgChildForeachFunc func,
					 gpointer        user_data);
/* Free an array allocated with gdam_arg_parse_from_buffer or
 * gdam_arg_parse_from_vararg. */
void          gdam_arg_free_array       (int             num_args,
                                         GdamArg        *args);
GdamArg      *gdam_arg_copy_array       (int             num_args,
                                         GdamArg        *arg);
GdamArg      *gdam_arg_scan             (int             num_args,
                                         GdamArg        *array,
                                         const char     *name,
					 GdamArgType     type);
int           gdam_arg_scan_int         (int             num_args,
                                         GdamArg        *array,
                                         const char     *name,
					 int             default_value);
gint64        gdam_arg_scan_int64       (int             num_args,
                                         GdamArg        *array,
                                         const char     *name,
					 gint64          default_value);
gdouble       gdam_arg_scan_double      (int             num_args,
                                         GdamArg        *array,
                                         const char     *name,
					 gdouble         default_value);
const char   *gdam_arg_scan_string      (int             num_args,
                                         GdamArg        *array,
                                         const char     *name,
					 const char     *default_value);

/* Clear an arguments allocations without freeing the argument. */
void          gdam_arg_destruct         (int             num_args,
                                         GdamArg        *arg);

void          gdam_arg_register         (GtkObjectClass *object_class,
                                         const char     *argname,
                                         int             type,
                                         GdamArgSetFunc  setter,
                                         GdamArgGetFunc  getter,
					 GdamArgValidator validator,
					 gpointer        validation_data,
					 int             gdam_arg_options);

void          gdam_arg_function_register(GtkObjectClass *object_class,
                                         const char     *function_name,
					 GdamArgGenericFunc  function);

enum {
	GDAM_ARG_CHILD_EXACT         = (1<<0),
	GDAM_ARG_CHILD_NUMBERED      = (1<<1)
};
void          gdam_arg_child_register   (GtkObjectClass *object_class,
                                         const char     *child_name,
					 GdamArgSetChildFunc set_func,
					 GdamArgGetChildFunc get_func,
					 GtkType         child_type,
					 int             child_flags);

/* Functions to track children.
 *
 *       trap:      get notification when an object has children
 *                  added or removed.
 *       removed:   notify the system that you are removing a child.
 *       added:     notify the system that you are adding a child.
 */
guint         gdam_arg_child_trap       (GtkObject      *object,
                                         GdamArgChildFunc add_func,
                                         GdamArgChildFunc remove_func,
                                         gpointer        user_data);
void          gdam_arg_child_trap_remove(GtkObject      *object,
                                         guint           trap_id);
void          gdam_arg_child_removed    (GtkObject      *object,
                                         GtkObject      *child,
                                         const char     *child_name);
void          gdam_arg_child_added      (GtkObject      *object,
                                         GtkObject      *child,
                                         const char     *child_name);


void          gdam_arg_class_function_register
                                        (GtkObjectClass *object_class,
                                         const char     *function_name,
                                         GdamArgGenericClassFunc  function);
gboolean      gdam_arg_call_class_function
                                        (GtkObjectClass *object_class,
                                         const char     *function_name,
                                         int             num_args_in,
                                         GdamArg        *args_in,
                                         int            *num_args_out,
                                         GdamArg       **args_out);



/* Returns 0 for nonexistant argument, or GDAM_ARG_TYPE_*. */
int           gdam_arg_query_type       (GtkType         object_type, 
                                         const char     *arg_name);

gboolean      gdam_arg_get_object_arg   (GtkObject      *object,
                                         GdamArg        *arg);
void          gdam_arg_set_object_arg   (GtkObject      *object,
                                         GdamArg        *arg);
void          gdam_arg_set              (GtkObject      *object,
                                         const char     *name,
					 /* value... */
					 ...);


/* Generic child getting/setting stuff. */
void          gdam_arg_get_children     (GtkObject      *object, 
                                         gboolean        recursive,
                                         int            *num_children_out,
                                         char         ***names_out,
                                         GtkObject    ***children);
GtkObject    *gdam_arg_get_child        (GtkObject      *object, 
                                         const char     *name,
                                         int             child_index);
void          gdam_arg_set_child        (GtkObject      *object,
                                         const char     *name,
                                         int             child_index,
                                         GtkObject      *child);
void          gdam_arg_remove_child     (GtkObject      *object,
                                         const char     *child_name,
					 int             child_index);


/* Getting notification when arguments change;
 * if you pass in NULL for ``name'', all changes
 * to this object are watched. */
guint         gdam_arg_connect          (GtkObject      *object,
                                         const char     *name,
                                         GdamArgFunc     func,
					 gpointer        user_data);
void          gdam_arg_disconnect       (GtkObject      *object,
                                         guint           connect_id);
void          gdam_arg_block            (GtkObject      *object,
                                         guint           connect_id);
void          gdam_arg_unblock          (GtkObject      *object,
                                         guint           connect_id);

/* Getting some or all arguments. */
void          gdam_arg_get_by_name      (GtkObject      *object,
                                         int             num_args,
                                         const char*const *arg_names,
                                         int            *num_args_out,
                                         GdamArg       **args_out);
void          gdam_arg_get_all          (GtkObject      *object,
                                         gboolean        only_vital,
					 gboolean        deprecated,
                                         int            *num_args_out,
                                         GdamArg       **args_out);

/* Calling functions. */
gboolean      gdam_arg_call_function    (GtkObject      *object,
                                         const char     *function_name,
                                         int             num_args,
                                         GdamArg        *args,
                                         int            *num_rv_out,
                                         GdamArg       **rv_out);
gboolean  gdam_arg_get_default_validator(GdamArgType      type,
                                         const char      *validation_string,
                                         GdamArgValidator *validator_out,
                                         gpointer       *validation_data_out);

/* --- Arg casting macros (and debug support) --- */
#define GDAM_ARG_STRING_FAST(arg) 		((arg)->d.string_data)
#define GDAM_ARG_INT_FAST(arg)    		((arg)->d.int_data)
#define GDAM_ARG_INT64_FAST(arg)  		((arg)->d.int64_data)
#define GDAM_ARG_DOUBLE_FAST(arg) 		((arg)->d.double_data)

#define GDAM_ARG_STRING_ARRAY_FAST(arg) 	((arg)->d.string_array)
#define GDAM_ARG_INT_ARRAY_FAST(arg)    	((arg)->d.int_array)
#define GDAM_ARG_INT64_ARRAY_FAST(arg)  	((arg)->d.int64_array)
#define GDAM_ARG_DOUBLE_ARRAY_FAST(arg) 	((arg)->d.double_array)

#if G_CAN_INLINE
G_INLINE_FUNC GdamArg *
gdam_arg_check_arg_type_internal (GdamArg *arg,
				  GdamArgType type,
				  const char *fund_type_name,
				  const char *filename,
				  guint lineno)
{
  if (arg->type != type)
    {
      if (arg->name != NULL)
	g_warning ("%s: %d: argument %s cannot be casted to %s",
		   filename, lineno, arg->name, fund_type_name);
      else
	g_warning ("%s: %d: argument cannot be casted to %s",
		   filename, lineno, fund_type_name);
    }
  return arg;
}
#else
GdamArg * gdam_arg_check_arg_type_internal (GdamArg *arg,
				            GdamArgType type,
				            const char *fund_type_name,
				            const char *filename,
				            guint lineno);
#endif

#define _GDAM_ARG_SLOW(arg, upper, lower) 				    \
	  GDAM_ARG_ ## upper ## _FAST (gdam_arg_check_arg_type_internal     \
				        (arg, GDAM_ARG_TYPE_ ## upper,	    \
					 #lower, __FILE__, __LINE__))
#define GDAM_ARG_STRING_SLOW(arg) 		_GDAM_ARG_SLOW (arg, STRING, string)
#define GDAM_ARG_INT_SLOW(arg) 			_GDAM_ARG_SLOW (arg, INT, int)
#define GDAM_ARG_INT64_SLOW(arg) 		_GDAM_ARG_SLOW (arg, INT64, int64)
#define GDAM_ARG_DOUBLE_SLOW(arg) 		_GDAM_ARG_SLOW (arg, DOUBLE, double)
#define GDAM_ARG_STRING_ARRAY_SLOW(arg) 	_GDAM_ARG_SLOW (arg, STRING_ARRAY, string_array)
#define GDAM_ARG_INT_ARRAY_SLOW(arg) 		_GDAM_ARG_SLOW (arg, INT_ARRAY, int_array)
#define GDAM_ARG_INT64_ARRAY_SLOW(arg) 		_GDAM_ARG_SLOW (arg, INT64_ARRAY, int64_array)
#define GDAM_ARG_DOUBLE_ARRAY_SLOW(arg) 	_GDAM_ARG_SLOW (arg, DOUBLE_ARRAY, double_array)

#if GDAM_DEBUG
#define GDAM_ARG_INT(arg)		GDAM_ARG_INT_SLOW(arg)
#define GDAM_ARG_INT64(arg)		GDAM_ARG_INT64_SLOW(arg)
#define GDAM_ARG_DOUBLE(arg)		GDAM_ARG_DOUBLE_SLOW(arg)
#define GDAM_ARG_STRING(arg)		GDAM_ARG_STRING_SLOW(arg)
#define GDAM_ARG_INT_ARRAY(arg)		GDAM_ARG_INT_ARRAY_SLOW(arg)
#define GDAM_ARG_INT64_ARRAY(arg)	GDAM_ARG_INT64_ARRAY_SLOW(arg)
#define GDAM_ARG_DOUBLE_ARRAY(arg)	GDAM_ARG_DOUBLE_ARRAY_SLOW(arg)
#define GDAM_ARG_STRING_ARRAY(arg)	GDAM_ARG_STRING_ARRAY_SLOW(arg)
#else
#define GDAM_ARG_INT(arg)		GDAM_ARG_INT_FAST(arg)
#define GDAM_ARG_INT64(arg)		GDAM_ARG_INT64_FAST(arg)
#define GDAM_ARG_DOUBLE(arg)		GDAM_ARG_DOUBLE_FAST(arg)
#define GDAM_ARG_STRING(arg)		GDAM_ARG_STRING_FAST(arg)
#define GDAM_ARG_INT_ARRAY(arg)		GDAM_ARG_INT_ARRAY_FAST(arg)
#define GDAM_ARG_INT64_ARRAY(arg)	GDAM_ARG_INT64_ARRAY_FAST(arg)
#define GDAM_ARG_DOUBLE_ARRAY(arg)	GDAM_ARG_DOUBLE_ARRAY_FAST(arg)
#define GDAM_ARG_STRING_ARRAY(arg)	GDAM_ARG_STRING_ARRAY_FAST(arg)
#endif

#endif
