#include <math.h>
#include "artsflow.h"
#include "stdsynthmodule.h"
#include "gst_artsio.h"
#include "convert.h"
#include "connect.h"
#include "flowsystem.h"

#include <gst/gst.h>

using namespace Arts;

namespace Gst {

class ArtsStereoSink_impl : virtual public ArtsStereoSink_skel,
			    virtual public StdSynthModule
{

  GstPad *sinkpad;
  GstPad *srcpad;
  unsigned long remainingsamples;
  GstData *inbuf;
  unsigned char *dataptr;

public:

  ArtsStereoSink_impl()
  {
    remainingsamples = 0;
    inbuf = NULL;
    dataptr = NULL;
  }

  void calculateBlock (unsigned long samples)
  {
    unsigned long fulfilled = 0;
//gint16 *s;
//fprintf(stderr,"StereoSink: getting %d samples\n",samples);

    while (fulfilled < samples) {
      if (remainingsamples == 0) {
//fprintf(stderr,"need to get a buffer\n");
	if (inbuf) {
	  gst_data_unref(inbuf);
	  inbuf = NULL;
	}

	// start by pulling a buffer from GStreamer
	inbuf = gst_pad_pull (sinkpad);
        while (GST_IS_EVENT (inbuf)) {
          switch (GST_EVENT_TYPE (inbuf)) {
          case GST_EVENT_EOS:
            gst_element_set_eos (GST_PAD_PARENT (sinkpad));
          default:
            break;
          }
          gst_pad_event_default (srcpad, GST_EVENT(inbuf));
          inbuf = gst_pad_pull (sinkpad);
        }

	dataptr = GST_BUFFER_DATA(GST_BUFFER(inbuf));
	remainingsamples = GST_BUFFER_SIZE(GST_BUFFER(inbuf)) / 4;
//fprintf(stderr,"got a buffer with %d samples\n",remainingsamples);
      }

      unsigned long count = MIN(remainingsamples,samples-fulfilled);
//fprintf(stderr,"have %d samples left, can fill %d\n",remainingsamples,count);
      convert_stereo_i16le_2float(count,dataptr,outleft,outright);
//s = (gint16 *)dataptr;
//fprintf(stderr,"samples in are %d and %d, out are %f and %f\n",s[0],s[1],outleft[0],outright[0]);
      remainingsamples -= count;
      dataptr += 4 * count;
      fulfilled += count;
    }
  }


  void setPad(GstPad *pad)
  {
    sinkpad = pad;
  }
  void setSrcPad(GstPad *pad)
  {
    srcpad = pad;
  }
};


class ArtsStereoSrc_impl : virtual public ArtsStereoSrc_skel,
			    virtual public StdSynthModule
{

  GstPad *srcpad;
  GstBuffer *outbuf;
  unsigned char *dataptr;

public:

  void calculateBlock (unsigned long samples)
  {
//gint16 *s;
//fprintf(stderr,"StereoSrc: handed %d samples\n",samples);
    outbuf = gst_buffer_new();
    GST_BUFFER_DATA(outbuf) = (guchar *)g_malloc(samples*4);
    GST_BUFFER_SIZE(outbuf) = samples*4;
    memset(GST_BUFFER_DATA(outbuf),0,samples*4);
    convert_stereo_2float_i16le(samples,inleft,inright,GST_BUFFER_DATA(outbuf));
//s = (gint16 *)GST_BUFFER_DATA(outbuf);
//fprintf(stderr,"samples in are %f and %f, out are %d and %d\n",inleft[0],inright[0],s[0],s[1]);
    gst_pad_push(srcpad,GST_DATA(outbuf));
    outbuf = NULL;
  }


  void setPad(GstPad *pad)
  {
    srcpad = pad;
  }
};

class GstArtsWrapper {
  Dispatcher *dispatcher;
  ArtsStereoSink sink;
  ArtsStereoSrc source;
  StereoVolumeControl effect;

public:
  GstArtsWrapper(GstPad *sinkpad, GstPad *sourcepad) {
    dispatcher = new Arts::Dispatcher();
    ArtsStereoSink_impl *sink_impl = new ArtsStereoSink_impl();
    ArtsStereoSrc_impl *source_impl = new ArtsStereoSrc_impl();
    sink_impl->setPad(sinkpad);
    sink_impl->setSrcPad(sourcepad);
    source_impl->setPad(sourcepad);
    sink = ArtsStereoSink::_from_base(sink_impl);
    source = ArtsStereoSrc::_from_base(source_impl);
    sink.start();
    effect.start();
    source.start();
    effect.scaleFactor(0.5);
    connect(sink, effect);
    connect(effect, source);
//    connect(sink,source);
  }
  void iterate()
  {
    source._node()->requireFlow();
  }
};


};


extern "C" {

void *gst_arts_wrapper_new(GstPad *sinkpad, GstPad *sourcepad)
{
  return new Gst::GstArtsWrapper(sinkpad, sourcepad);
}

void gst_arts_wrapper_free(void *wrapper)
{
  Gst::GstArtsWrapper *w = (Gst::GstArtsWrapper *)wrapper;
  delete w;
}

void gst_arts_wrapper_do(void *wrapper)
{
  Gst::GstArtsWrapper *w = (Gst::GstArtsWrapper *)wrapper;
  w->iterate();
}

}

// vim:sts=2:sw=2