#include <gst/gst.h>

GstPad *srcpad, *sinkpad;
GstPad *srcconvpad, *sinkconvpad;
GstPadTemplate *srcpadtempl, *sinkpadtempl;
GstPadTemplate *srcconvtempl, *sinkconvtempl;

gint converter_in = -1, converter_out = -1;
gint target_rate = 2000;

static GstPadTemplate*
src_factory (void)
{
  return 
    gst_padtemplate_new (
      "src",
      GST_PAD_SRC,
      GST_PAD_ALWAYS,
      gst_caps_new (
        "test_src",
        "audio/raw",
	gst_props_new (
          "rate",    GST_PROPS_INT_RANGE (16, 20000),
	  NULL)),
      NULL);
}

static GstPadTemplate*
src_conv_factory (void)
{
  return 
    gst_padtemplate_new (
      "src",
      GST_PAD_SRC,
      GST_PAD_ALWAYS,
      gst_caps_new (
        "test_src",
        "audio/raw",
	gst_props_new (
          "rate",    GST_PROPS_INT_RANGE (16, 20000),
	  NULL)),
      NULL);
}

static GstPadTemplate*
sink_conv_factory (void)
{
  return 
    gst_padtemplate_new (
      "src",
      GST_PAD_SINK,
      GST_PAD_ALWAYS,
      gst_caps_new (
        "test_src",
        "audio/raw",
	gst_props_new (
          "rate",    GST_PROPS_INT_RANGE (16, 20000),
	  NULL)),
      NULL);
}

static GstPadTemplate*
sink_factory (void)
{
  return 
    gst_padtemplate_new (
      "sink",
      GST_PAD_SINK,
      GST_PAD_ALWAYS,
      gst_caps_new (
        "test_sink",
        "audio/raw",
	gst_props_new (
          "rate",    GST_PROPS_INT_RANGE (16, 20000),
	  NULL)),
      NULL);
}

static GstCaps*
sink_caps (void)
{
  return 
    gst_caps_new (
      "sink_caps",
      "audio/raw",
      gst_props_new (
        "rate",     GST_PROPS_INT (6000),
	NULL));
}

static GstCaps*
src_caps (void)
{
  return 
    gst_caps_new (
      "src_caps",
      "audio/raw",
      gst_props_new (
        "rate",     GST_PROPS_INT (3000),
	NULL));
}

static GstPadTemplate *srctempl, *sinktempl;
static GstCaps *srccaps, *sinkcaps;

static GstPadNegotiateReturn
converter_negotiate_src (GstPad *pad, GstCaps **caps, gpointer *data)
{
  g_print (">");

  if (*data == NULL) {
    *caps = NULL;
    return GST_PAD_NEGOTIATE_TRY;
  }
  if (*caps) {
    converter_out = gst_caps_get_int (*caps, "rate");
    return GST_PAD_NEGOTIATE_AGREE;
  }

  return GST_PAD_NEGOTIATE_FAIL;
}

static GstPadNegotiateReturn
converter_negotiate_sink (GstPad *pad, GstCaps **caps, gpointer *data)
{
  g_print ("<");
  if (*data == NULL) {
    *caps = GST_PAD_CAPS (srcconvpad);
    return GST_PAD_NEGOTIATE_TRY;
  }
  if (*caps) {
    converter_in = gst_caps_get_int (*caps, "rate");

    if (*data == 1) {
      converter_out = gst_caps_get_int (*caps, "rate");
      return gst_pad_negotiate_proxy (pad, srcconvpad, caps);
    }
    return GST_PAD_NEGOTIATE_AGREE;
  }

  return GST_PAD_NEGOTIATE_FAIL;
}

static GstPadNegotiateReturn
target_negotiate_sink (GstPad *pad, GstCaps **caps, gpointer *data)
{
  g_print ("{");
  if (*data == NULL) {
    *caps = gst_caps_new (
		    "target_caps",
		    "audio/raw",
		    gst_props_new (
			    "rate", GST_PROPS_INT (target_rate),
			    NULL)
		    );
    return GST_PAD_NEGOTIATE_TRY;
  }
  if (*caps) {
    target_rate = gst_caps_get_int (*caps, "rate");
    g_print ("target set %d\n", target_rate);
    return GST_PAD_NEGOTIATE_AGREE;
  }

  return GST_PAD_NEGOTIATE_FAIL;
}

int 
main (int argc, char *argv[])
{
  gboolean overall = TRUE;
  gboolean result;
  
  gst_init (&argc, &argv);

  srctempl = src_factory ();
  sinktempl = sink_factory ();
  srcpad = gst_pad_new_from_template (srctempl, "src");
  sinkpad = gst_pad_new_from_template (sinktempl, "sink");

  srcconvtempl = src_conv_factory ();
  sinkconvtempl = sink_conv_factory ();
  srcconvpad = gst_pad_new_from_template (srcconvtempl, "csrc");
  sinkconvpad = gst_pad_new_from_template (sinkconvtempl, "csink");

  gst_pad_set_negotiate_function (srcconvpad, converter_negotiate_src);
  gst_pad_set_negotiate_function (sinkconvpad, converter_negotiate_sink);
  gst_pad_set_negotiate_function (sinkpad, target_negotiate_sink);

  sinkcaps  = sink_caps ();
  srccaps  = src_caps ();

  g_print ("-------)      (-----------)       (-----   \n");
  g_print ("       !      ! converter !       !        \n");
  g_print ("      src -- csink       csrc -- sink      \n");
  g_print ("-------)      (-----------)       (-----   \n\n");
  g_print ("The convertor first tries to proxy the caps received\n");
  g_print ("on its csink pad to its csrc pad, when that fails, it\n");
  g_print ("sets up the conversion.\n\n");
  

  g_print ("sink pad set caps (rate=%d), converter status: %d %d\n", target_rate, 
		  converter_in, converter_out);
  gst_caps_set (sinkcaps, "rate", GST_PROPS_INT (target_rate));
  result = gst_pad_set_caps (sinkpad, sinkcaps);
  g_print ("result: %d, converter status: %d %d, target: %d\n\n", result, 
		  converter_in, converter_out, target_rate);

  result = gst_pad_connect (srcpad, sinkconvpad);
  g_print ("pad connect 1: %d\n", result);
  overall &= (result == TRUE);
  result = gst_pad_connect (srcconvpad, sinkpad);
  g_print ("pad connect 2: %d\n", result);
  overall &= (result == TRUE);

  g_print ("after connect, converter status: %d %d, target %d\n\n", converter_in, converter_out, target_rate);

  g_print ("src pad set caps (rate=%d), converter status: %d %d, target %d \n", gst_caps_get_int (srccaps, "rate"),
		  converter_in, converter_out, target_rate);
  result = gst_pad_set_caps (srcpad, srccaps);
  g_print ("result %d, converter status: %d %d, target %d\n\n", result, 
		  converter_in, converter_out, target_rate);

  g_print ("sink pad set caps (rate=2000), converter status: %d %d, target %d \n",
		  converter_in, converter_out, target_rate);
  target_rate = 2000;
  gst_caps_set (sinkcaps, "rate", GST_PROPS_INT (target_rate));
  result = gst_pad_set_caps (sinkpad, sinkcaps);
  g_print ("result %d, converter status: %d %d, target: %d\n\n", result, 
		  converter_in, converter_out, target_rate);

  gst_caps_set (srccaps, "rate", GST_PROPS_INT (4000));
  result = gst_pad_renegotiate (srcpad);
  g_print ("sink pad renegotiate caps %d, converter status: %d %d, target: %d\n", result, 
		  converter_in, converter_out, target_rate);

  gst_caps_set (srccaps, "rate", GST_PROPS_INT (40000));
  result = gst_pad_set_caps (srcpad, srccaps);
  g_print ("sink pad set caps %d, converter status: %d %d, target: %d\n", result, 
		  converter_in, converter_out, target_rate);

  gst_caps_set (sinkcaps, "rate", GST_PROPS_INT (40000));
  result = gst_pad_set_caps (sinkpad, sinkcaps);
  g_print ("sink pad set caps %d, converter status: %d %d, target: %d\n", result, 
		  converter_in, converter_out, target_rate);

  target_rate = 9000;
  gst_caps_set (sinkcaps, "rate", GST_PROPS_INT (target_rate));
  result = gst_pad_set_caps (sinkpad, sinkcaps);
  g_print ("sink pad set caps %d, converter status: %d %d, target: %d\n", result, 
		  converter_in, converter_out, target_rate);

  exit (!overall);
}