#include <gtk/gtk.h>
#include <gst/interfaces/mixer.h>
#include <gst/audio/mixerutils.h>



static GtkWidget *channel_labels[10] = { NULL, };



static GstElement *
query_mixer (void)
{
  GstElement *mixer = NULL;
  GList      *mixers = NULL;
  
  mixers = gst_audio_default_registry_mixer_filter ((GstAudioMixerFilterFunc) gtk_true, 
                                                    TRUE, NULL);
  if (g_list_length (mixers) > 0)
    mixer = g_list_first (mixers)->data;
  g_list_free (mixers);

  return mixer;
}



static void
fader_changed (GtkRange *range)
{
  GstElement    *mixer;
  GstMixerTrack *track;
  gint          *volumes;
  gint           min_volume;
  gint           max_volume;
  gint           num_channels;
  gint           i;

  mixer = g_object_get_data (G_OBJECT (range), "mixer");
  track = g_object_get_data (G_OBJECT (range), "track");

  g_object_get (track, 
                "min-volume", &min_volume,
                "max-volume", &max_volume,
                "num-channels", &num_channels,
                NULL);

  volumes = g_new0 (gint, num_channels);
  for (i = 0; i < num_channels; ++i)
    volumes[i] = MAX (min_volume, MIN (max_volume, (gint) gtk_range_get_value (range)));

  g_debug ("set volume of PCM (%i-%i) to %i", min_volume, max_volume, volumes[0]);

  gst_mixer_set_volume (GST_MIXER (mixer), track, volumes);

  g_free (volumes);
}



static void
bus_message (GstBus     *bus,
             GstMessage *message,
             GtkRange   *range)
{
  GstMixerMessageType type;
  GstMixerTrack      *message_track;
  GstMixerTrack      *track;
  GstElement         *mixer;
  gchar              *label_text;
  gint               *volumes;
  gint                num_channels;
  gint                i;

  mixer = g_object_get_data (G_OBJECT (range), "mixer");
  track = g_object_get_data (G_OBJECT (range), "track");

  g_debug ("bus message: %s", GST_MESSAGE_TYPE_NAME (message));
  g_debug ("  belongs to our mixer: %s", 
           GST_ELEMENT (GST_MESSAGE_SRC (message)) == mixer ? "yes" : "no");

  if (GST_ELEMENT (GST_MESSAGE_SRC (message)) != mixer)
    return;

  type = gst_mixer_message_get_type (message);

  g_debug ("  message type is volume change: %s", 
           type == GST_MIXER_MESSAGE_VOLUME_CHANGED ? "yes" : "no");

  if (type != GST_MIXER_MESSAGE_VOLUME_CHANGED)
    return;

  gst_mixer_message_parse_volume_changed (message, &message_track, &volumes, &num_channels);

  g_debug ("  track is PCM: %s", message_track == track ? "yes" : "no");

  if (message_track != track)
    return;

  for (i = 0; i < num_channels && i < 10; ++i)
    {
      label_text = g_strdup_printf ("CH%2d: %i", i, volumes[i]);
      gtk_label_set_text (GTK_LABEL (channel_labels[i]), label_text);
      g_free (label_text);
    }
}



static GtkWidget *
create_mixer_window (GstElement *mixer)
{
  GtkWidget         *window;
  GtkWidget         *vbox;
  GtkWidget         *hbox;
  GtkWidget         *vbox1;
  GtkWidget         *label;
  GtkWidget         *scale;
  GstElementFactory *factory;
  GstBus            *bus;
  const GList       *tracks;
  const GList       *iter;
  gchar             *device_name;
  gchar             *mixer_name;
  gchar             *track_name;
  gchar             *label_text;
  gint               min_volume;
  gint               max_volume;
  gint               num_channels;
  gint               i;

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_default_size (GTK_WINDOW (window), 100, 300);
  g_signal_connect (window, "delete-event", G_CALLBACK (gtk_main_quit), NULL);

  vbox = gtk_vbox_new (FALSE, 6);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
  gtk_container_add (GTK_CONTAINER (window), vbox);
  gtk_widget_show (vbox);

  factory = gst_element_get_factory (GST_ELEMENT (mixer));
  g_object_get (mixer, "device-name", &device_name, NULL);
  mixer_name = g_strdup_printf ("%s (%s)", device_name, 
                                gst_element_factory_get_longname (factory));
  g_free (device_name);

  label = gtk_label_new (mixer_name);
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

  g_free (mixer_name);

  hbox = gtk_hbox_new (FALSE, 12);
  gtk_container_add (GTK_CONTAINER (vbox), hbox);
  gtk_widget_show (hbox);

  tracks = gst_mixer_list_tracks (GST_MIXER (mixer));
  for (iter = tracks; iter != NULL; iter = g_list_next (iter))
    {
      g_object_get (iter->data, "label", &track_name, NULL);

      if (!g_str_equal (track_name, "PCM"))
        {
          g_free (track_name);
          continue;
        }

      g_object_get (iter->data, 
                    "min-volume", &min_volume,
                    "max-volume", &max_volume,
                    "num-channels", &num_channels,
                    NULL);

      scale = gtk_vscale_new_with_range (min_volume, max_volume, 1);
      g_signal_connect (scale, "value-changed", G_CALLBACK (fader_changed), NULL);
      gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
      gtk_container_add (GTK_CONTAINER (hbox), scale);
      gtk_widget_show (scale);

      vbox1 = gtk_vbox_new (FALSE, 6);
      gtk_container_add (GTK_CONTAINER (hbox), vbox1);
      gtk_widget_show (vbox1);

      for (i = 0; i < num_channels && i < 10; ++i)
        {
          label_text = g_strdup_printf ("CH%2d:", i);
          channel_labels[i] = gtk_label_new (label_text);
          g_free (label_text);
          gtk_box_pack_start (GTK_BOX (vbox1), channel_labels[i], FALSE, FALSE, 0);
          gtk_widget_show (channel_labels[i]);
        }

      g_object_set_data (G_OBJECT (scale), "mixer", mixer);
      g_object_set_data (G_OBJECT (scale), "track", iter->data);

      bus = gst_bus_new ();
      g_signal_connect (bus, "message::element", G_CALLBACK (bus_message), scale);
      gst_bus_add_signal_watch (bus);
      gst_element_set_bus (mixer, bus);

      g_free (track_name);
      
      break;
    }

  return window;
}



int 
main (int argc, char **argv)
{
  GtkWidget  *window;
  GstElement *mixer;

  gst_init (&argc, &argv);
  gtk_init (&argc, &argv);

  mixer = query_mixer ();
  window = create_mixer_window (mixer);
  gtk_widget_show (window);

  gtk_main ();

  return 0;
}
