#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#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