/*
 * SwamiControl.h - Swami control base object
 *
 * Swami
 * Copyright (C) 1999-2014 Element Green <element@elementsofsound.org>
 *
 * This program 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; version 2
 * of the License only.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA or point your web browser to http://www.gnu.org.
 */
#ifndef __SWAMI_CONTROL_H__
#define __SWAMI_CONTROL_H__

typedef struct _SwamiControl SwamiControl;
typedef struct _SwamiControlClass SwamiControlClass;

//#include <sys/time.h>
#include <glib.h>
#include <glib-object.h>
#include <libswami/SwamiLock.h>
#include <libswami/SwamiControlQueue.h>
#include <libinstpatch/IpatchList.h>
#include <libswami/SwamiControlEvent.h>
#include <libswami/SwamiParam.h>

#define SWAMI_TYPE_CONTROL  (swami_control_get_type ())
#define SWAMI_CONTROL(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWAMI_TYPE_CONTROL, SwamiControl))
#define SWAMI_CONTROL_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST ((klass), SWAMI_TYPE_CONTROL, \
   SwamiControlClass))
#define SWAMI_IS_CONTROL(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWAMI_TYPE_CONTROL))
#define SWAMI_IS_CONTROL_CLASS(obj) \
  (G_TYPE_CHECK_CLASS_TYPE ((obj), SWAMI_TYPE_CONTROL))
#define SWAMI_CONTROL_GET_CLASS(obj) \
  (G_TYPE_INSTANCE_GET_CLASS (obj, SWAMI_TYPE_CONTROL, SwamiControlClass))


/* Swami control object */
struct _SwamiControl
{
    SwamiLock parent_instance;	/* derived from SwamiLock */

    guint flags;			/* flags field (SwamiControlFlags) */
    GList *active;		/* active event propagations */
    SwamiControlQueue *queue;	/* event queue or NULL if no queuing */
    SwamiControl *master;	/* control to slave parameter spec to or NULL */
    GType value_type;	  /* control value type (or 0 for wildcard) */

    /* lists of SwamiControlConn structures (defined in SwamiControl.c) */
    GSList *inputs;	    /* list of input connections (readable) */
    GSList *outputs;	   /* list of output connections (writable) */
};

/**
 * SwamiControlGetSpecFunc:
 * @control: Control to get parameter spec for
 *
 * A #SwamiControlClass optional method type to get the parameter spec
 * from a control. If this method is not used or it returns %NULL then
 * the control is assumed to accept control events of any type.
 *
 * Note: The control is locked while calling this method. Also, this
 * method should return the parameter spec quickly, to speed up the event
 * emission process.
 *
 * Returns: Should return the parameter specification for the control or
 * %NULL for type independent controls. Reference count should not be modified,
 * this is handled by the caller.
 */
typedef GParamSpec *(*SwamiControlGetSpecFunc)(SwamiControl *control);

/**
 * SwamiControlSetSpecFunc:
 * @control: Control to get parameter spec for
 * @pspec: Parameter specification to assign
 *
 * A #SwamiControlClass optional method type to set the parameter spec
 * for a control.  The method should call g_param_spec_ref() followed by
 * g_param_spec_sink () to take over the reference.
 * If a control has a specific value type and the
 * #SWAMI_CONTROL_SPEC_NO_CONV is not set then the parameter spec is
 * converted to the parameter spec type used for the given value type and the
 * min/max/default values of the parameter spec will be processed with the
 * set_trans value transform function if assigned,
 * otherwise the parameter spec is passed as is without conversion.
 *
 * Note: The control is locked while calling this method.
 *
 * Returns: Should return %TRUE if parameter spec change was allowed,
 * %FALSE otherwise.
 */
typedef gboolean(*SwamiControlSetSpecFunc)(SwamiControl *control,
        GParamSpec *pspec);
/**
 * SwamiControlGetValueFunc:
 * @control: Control to get value from
 * @value: Caller supplied value to store control value in
 *
 * A #SwamiControlClass optional method type to get the value from a
 * value control that is readable (#SWAMI_CONTROL_SENDS flag must be
 * set). The @value has been initialized to the native type of the
 * control's parameter spec. If this method is used then
 * #SwamiControlGetSpecFunc must also be set.
 *
 * Note: The control is not locked when calling this method.
 */
typedef void (*SwamiControlGetValueFunc)(SwamiControl *control, GValue *value);

/**
 * SwamiControlSetValueFunc:
 * @control: Control that is receiving an event
 * @event: Control event being received
 * @value: Value which is being received (possibly converted from original
 *   event value depending on the control's settings)
 *
 * A #SwamiControlClass optional method type to receive control
 * values. If the #SWAMI_CONTROL_NO_CONV flag is not set for this
 * control and the #SwamiControlGetSpecFunc returns a parameter spec
 * then @value will be the result of the original event value
 * converted to the control's native type.
 *
 * This method gets called for events received via a control's inputs
 * or when swami_control_set_event() or swami_control_set_value() is called.
 *
 * Note: The control is not locked during this method call.
 */
typedef void (*SwamiControlSetValueFunc)(SwamiControl *control,
        SwamiControlEvent *event,
        const GValue *value);

struct _SwamiControlClass
{
    SwamiLockClass parent_class;

    /* signals */
    void (*connect)(SwamiControl *c1, SwamiControl *c2, guint flags);
    void (*disconnect)(SwamiControl *c1, SwamiControl *c2, guint flags);

    /* methods */
    SwamiControlGetSpecFunc get_spec;
    SwamiControlSetSpecFunc set_spec;
    SwamiControlGetValueFunc get_value;
    SwamiControlSetValueFunc set_value;
};

typedef enum
{
    SWAMI_CONTROL_SENDS        = 1 << 0, /* control is readable/sends */
    SWAMI_CONTROL_RECVS        = 1 << 1, /* control is writable/receives */
    SWAMI_CONTROL_NO_CONV      = 1 << 2, /* don't convert incoming values */
    SWAMI_CONTROL_NATIVE       = 1 << 3, /* values of native value type only */
    SWAMI_CONTROL_VALUE        = 1 << 4, /* value control - queue optimization */
    SWAMI_CONTROL_SPEC_NO_CONV = 1 << 5  /* don't convert parameter spec type */
} SwamiControlFlags;

/* mask for user controlled flag bits */
#define SWAMI_CONTROL_FLAGS_USER_MASK 0x7F

/* a convenience value for send/receive controls */
#define SWAMI_CONTROL_SENDRECV  (SWAMI_CONTROL_SENDS | SWAMI_CONTROL_RECVS)

/* 7 bits used, 5 reserved */
#define SWAMI_CONTROL_UNUSED_FLAG_SHIFT 12

/* connection priority ranking (first 2 bits of flags field) */
typedef enum
{
    SWAMI_CONTROL_CONN_PRIORITY_DEFAULT = 0,
    SWAMI_CONTROL_CONN_PRIORITY_LOW     = 1,
    SWAMI_CONTROL_CONN_PRIORITY_MEDIUM  = 2,
    SWAMI_CONTROL_CONN_PRIORITY_HIGH    = 3
} SwamiControlConnPriority;

#define SWAMI_CONTROL_CONN_PRIORITY_MASK  (0x2)
#define SWAMI_CONTROL_CONN_DEFAULT_PRIORITY_VALUE \
  (SWAMI_CONTROL_CONN_PRIORITY_MEDIUM)

typedef enum /*< flags >*/
{
    SWAMI_CONTROL_CONN_INPUT  = 1 << 2, /* set for inputs (used internally) */
    SWAMI_CONTROL_CONN_OUTPUT = 1 << 3, /* set for outputs (used internally) */
    SWAMI_CONTROL_CONN_INIT   = 1 << 4, /* update value on connect */
    SWAMI_CONTROL_CONN_BIDIR  = 1 << 5, /* make a bi-directional connection */
    SWAMI_CONTROL_CONN_SPEC   = 1 << 6  /* synchronize the parameter spec on connect */
} SwamiControlConnFlags;

/* convenience combo flags */

/* #SWAMI_CONTROL_CONN_BIDIR and #SWAMI_CONTROL_CONN_INIT */
#define SWAMI_CONTROL_CONN_BIDIR_INIT \
  (SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_INIT)

/* #SWAMI_CONTROL_CONN_BIDIR, #SWAMI_CONTROL_CONN_SPEC and
 * #SWAMI_CONTROL_CONN_INIT */
#define SWAMI_CONTROL_CONN_BIDIR_SPEC_INIT \
  (SWAMI_CONTROL_CONN_BIDIR | SWAMI_CONTROL_CONN_SPEC | SWAMI_CONTROL_CONN_INIT)


GType swami_control_get_type(void);
SwamiControl *swami_control_new(void);

void swami_control_connect(SwamiControl *src, SwamiControl *dest,
                           guint flags);
void swami_control_connect_transform
(SwamiControl *src, SwamiControl *dest, guint flags,
 SwamiValueTransform trans1, SwamiValueTransform trans2,
 gpointer data1, gpointer data2,
 GDestroyNotify destroy1, GDestroyNotify destroy2);
void swami_control_connect_item_prop(SwamiControl *dest, GObject *object,
                                     GParamSpec *pspec);
void swami_control_disconnect(SwamiControl *src, SwamiControl *dest);
void swami_control_disconnect_all(SwamiControl *control);
void swami_control_disconnect_unref(SwamiControl *control);
IpatchList *swami_control_get_connections(SwamiControl *control,
        SwamiControlConnFlags dir);
void swami_control_set_transform(SwamiControl *src, SwamiControl *dest,
                                 SwamiValueTransform trans, gpointer data,
                                 GDestroyNotify destroy);
void swami_control_get_transform(SwamiControl *src, SwamiControl *dest,
                                 SwamiValueTransform *trans);

void swami_control_set_flags(SwamiControl *control, int flags);
int swami_control_get_flags(SwamiControl *control);
void swami_control_set_queue(SwamiControl *control, SwamiControlQueue *queue);
SwamiControlQueue *swami_control_get_queue(SwamiControl *control);

GParamSpec *swami_control_get_spec(SwamiControl *control);
gboolean swami_control_set_spec(SwamiControl *control, GParamSpec *pspec);
gboolean swami_control_sync_spec(SwamiControl *control, SwamiControl *source,
                                 SwamiValueTransform trans, gpointer data);
GParamSpec *
swami_control_transform_spec(SwamiControl *control, SwamiControl *source,
                             SwamiValueTransform trans, gpointer data);
void swami_control_set_value_type(SwamiControl *control, GType type);
void swami_control_get_value(SwamiControl *control, GValue *value);
void swami_control_get_value_native(SwamiControl *control, GValue *value);
void swami_control_set_value(SwamiControl *control, const GValue *value);
void swami_control_set_value_no_queue(SwamiControl *control,
                                      const GValue *value);
void swami_control_set_event(SwamiControl *control, SwamiControlEvent *event);
void swami_control_set_event_no_queue(SwamiControl *control,
                                      SwamiControlEvent *event);
void swami_control_set_event_no_queue_loop(SwamiControl *control,
        SwamiControlEvent *event);
void swami_control_transmit_value(SwamiControl *control, const GValue *value);
void swami_control_transmit_event(SwamiControl *control,
                                  SwamiControlEvent *event);
void swami_control_transmit_event_loop(SwamiControl *control,
                                       SwamiControlEvent *event);
void swami_control_do_event_expiration(void);
SwamiControlEvent *swami_control_new_event(SwamiControl *control,
        SwamiControlEvent *origin,
        const GValue *value);
#endif
