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


typedef struct _GdamMixSkin GdamMixSkin;
typedef struct _GdamMixSkinClass GdamMixSkinClass;
typedef struct _GdamMixSkinArg GdamMixSkinArg;
typedef struct _GdamMixSkinElement GdamMixSkinElement;
typedef struct _GdamMixSkinPrototype GdamMixSkinPrototype;

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

#include "gdamskin.h"
#include <gdam/component/gdammix.h>
#include <gtk/gtkentry.h>
#include <gtk/gtklabel.h>

GtkType gdam_mix_skin_get_type();
#define GDAM_TYPE_MIX_SKIN		(gdam_mix_skin_get_type ())
#define GDAM_MIX_SKIN(obj)              (GTK_CHECK_CAST ((obj), GDAM_TYPE_MIX_SKIN, GdamMixSkin))
#define GDAM_MIX_SKIN_CLASS(klass)      (GTK_CHECK_CLASS_CAST ((klass), GDAM_TYPE_MIX_SKIN, GdamMixSkinClass))
#define GDAM_IS_MIX_SKIN(obj)           (GTK_CHECK_TYPE ((obj), GDAM_TYPE_MIX_SKIN))
#define GDAM_IS_MIX_SKIN_CLASS(klass)   (GTK_CHECK_CLASS_TYPE ((klass), GDAM_TYPE_MIX_SKIN))

#define GDAM_MIX_SKIN_FACTORY_FAMILY	(gdam_mix_skin_get_factory_family())

typedef enum
{
  GDAM_MIX_SKIN_FLAGS_LOGARITHMIC = (1<<0)
} GdamMixSkinFlags;

struct _GdamMixSkinArg
{
  guint model_index;
  char *arg_name;
  gdouble min_bound;
  gdouble max_bound;
  GdamMixSkinFlags flags;
};


/*
 *           GdamMixSkinPrototype
 * 
 * A type of sample, whose parameters may vary.
 */
struct _GdamMixSkinPrototype
{
  GdamMixSkin            *mix_skin;
  GdamModel              *model;
  int                     num_submodels;

  /* eg `builder::component' */
  char                  **submodel_ident;
  GdamModel             **submodels;

  /* Whether to draw elements of this prototype. */
  gboolean                is_hidden;

  /* Arguments that are variable over Elements. */
  guint                   num_args;
  GdamMixSkinArg         *args;

  /* just a user-editable naming field,
   * to keep the users sane... */
  char                   *prototype_name;

  /* We to start drawing the number, relative to the upper/left
   * of the bar. */
  gboolean                has_string_offset;
  int                     string_offset_left;
  int                     string_offset_center;
  int                     string_offset_y;
  int                     string_width;

  /* The argument to render. */
  int                     selected_arg;

  /* This is the label for the name in the button bar. */
  GtkLabel               *name_label;

  /* The editable prototype;  only visible when
   * this prototype is the selected_prototype. [ie with
   * gtk_widget_{hide,show}] */
  GdamSkin               *skin;

  /* GdamMixElements of this type */
  GSList                 *elements;

  /* private: box containing control buttons for
   * this prototype. */
  GtkContainer           *button_box;
};

/*
 *           GdamMixSkinElement
 *
 * A single sample, a rectangle in the drawing area.
 * GdamMixSkin::skin_elements maps from the GdamMixElements
 * to the GdamMixSkinElements.
 */
struct _GdamMixSkinElement
{
  GdamMixSkinPrototype   *prototype;

  GdamMixElement         *element;

  /* As per the prototype... */
  GdamModel             **models;

  /* If the source is being dragged. */
  gboolean                is_dragging;

  /* Is the element selected. */
  gboolean                is_selected;

  /* the notification connect_ids for the various models. */
  guint                   child_trap_id;   /* for element->producer */
  guint                  *subchild_trap_ids;  /* for submodels[i] */

  /* The y-coordinate normalized to 0 .. 1,
   * to use when arg_index == -1. */
  gdouble                 natural_y_frac;

  /* Cached screen coordinates. */
  gboolean                screen_data_valid;
  int                     screen_x, screen_y;
  int                     screen_width, screen_height;

  /* Data which is only used when dragging.
   * (Is this excessive?  Perhaps it could be computed
   *  as needed...) */
  gboolean                drag_data_valid;
  int                     drag_x, drag_y;
  int                     drag_width, drag_height;
};

/*
 *           GdamMixSkinClass
 *
 * Virtual functions.
 */
struct _GdamMixSkinClass
{
  GdamSkinClass           skin_class;
};

/*
 *           GdamMixSkin
 *
 * A skin for a mixer.
 */
struct _GdamMixSkin
{
  GdamSkin                skin;

  int                     num_prototypes;
  GdamMixSkinPrototype  **prototypes;
  GSList                 *elements;

  /* A selection area. */
  gboolean                has_selection;
  GdkPoint                selection[2];
  GSList                 *selected_elements;
  int                     selection_mode;

  /* The selected prototype is the visible one;
   * it is inside of `prototype_container'. */
  GdamMixSkinPrototype   *selected_prototype;
  GtkContainer           *prototype_container;

  /* For the buttons which allow you to select
   * and delete a prototype. */
  GtkContainer           *prototype_menu_box;

  int                     sub_beats;

  /* The drawing area for the samples... */
  GtkWidget              *drawing_area;

  /* Font for the floating subsamples. */
  GdkFont                *font;

  /* The edit box to use to modify the prototype's name. */
  GtkWidget              *prototype_name_entry;

  /* Rendering information. */
  GdkGC                  *text_gc;
  GdkGC                  *rect_gc;
  GdkGC                  *rect_boundary_gc;
  GdkGC                  *grid_gc;
  GdkGC                  *background;
  GdkGC                  *selection_gc;
  GdkGC                  *drag_gc;

  /* Doublebuffering */
  GdkPixmap              *backing_pixmap;

  /* Map from GdamMixElements => GdamMixSkinElements */
  GHashTable             *skin_elements;
   
  /* If the user is dragging a subsample around,
   * that is indicated here. */
  GSList                 *dragging;
  int                     drag_start_x, drag_start_y;
  int                     drag_current_x, drag_current_y;

  /* Are the dragged elements from a selection buffer? */
  gboolean                was_dragging_selected;

  /* The boundaries of the viewable area. */
  gdouble                 min_beat;
  gdouble                 max_beat;

  /* These adjustments are for the zooming and panning.
   * Sigh... we should use a general method for this.... */
  GtkAdjustment          *time_pan_adj;
  GtkAdjustment          *time_zoom_adj;

  /* Buffer id's that are not supposed to be drawn. */
  GHashTable             *hidden_buffers;

  /* A map from buffer_ids => widget and back
   * for the box of buttons that allows
   * select-all-of-type, delete-all-of-type, etc. */
  GtkContainer           *buffer_id_holder;

  guint                   tempo_adj_connect;
  GtkAdjustment          *tempo_adj;

  /* Offset from the center of the rendering where the user clicked. */
  int                     drag_click_offset_x, drag_click_offset_y;

  /* Height and width of the drawing area. */
  int                     draw_width, draw_height;

  guint                   redraw_thread;

  /* Asynchronous beat tapping support. */
  gdouble                 asynch_position;
  gdouble                 asynch_step_size;
  GtkLabel               *asynch_position_label;
  gboolean                asynch_autostep;
  GtkToggleButton        *autostep_toggle_button;

  /* This should be removed if we're destroyed... */
  guint                   input_hook_id;
};

int    gdam_mix_skin_new_prototype   (GdamMixSkin        *mix_skin,
                                      const char         *model_spec,
                                      const char         *starting_name);
void   gdam_mix_skin_edit_prototype  (GdamMixSkin        *mix_skin,
                                      int                 prototype_id);
void   gdam_mix_skin_delete_prototype(GdamMixSkin        *mix_skin,
                                      int                 prototype_id);

/* hmm... this (or all these functions?) need to be made
 * consistent ... probably better to scrap prototype_id. */
void gdam_mix_skin_set_prototype_name(GdamMixSkin        *mix_skin,
                                      GdamMixSkinPrototype*,
                                      const char         *new_name);

int    gdam_mix_skin_prototype_index (GdamMixSkinPrototype*);

int    gdam_mix_skin_prototype_try_arg(GdamMixSkinPrototype *prototype,
				       guint                 model_index,
				       const char           *arg_name,
				       gdouble               min_bound,
				       gdouble               max_bound,
				       GdamMixSkinFlags      flags);
int    gdam_mix_skin_prototype_force_arg(GdamMixSkinPrototype *prototype,
				         guint                 model_index,
				         const char           *arg_name,
				         gdouble               min_bound,
				         gdouble               max_bound,
				         GdamMixSkinFlags      flags);

void   gdam_mix_skin_remove          (GdamMixSkin        *mix_skin,
                                      GdamMixSkinElement *element);


#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif
