/*
 * sound_gstreamer.cxx
 *
 * Sound driver implementation.
 *
 * Portable Windows Library
 *
 * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is Portable Windows Library.
 *
 * The Initial Developer of the Original GSTREAMER Code is 
 * Damien Sandras <dsandras@seconix.com>
 *
 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
 * All Rights Reserved.
 *
 * Contributor(s): /
 *
 * $Log: sound_gstreamer.cxx,v $
 */

#pragma implementation "sound_gstreamer.h"

#include "sound_gstreamer.h"


PCREATE_SOUND_PLUGIN(GSTREAMER, PSoundChannelGSTREAMER)


static void
cb_write_handoff (GstElement *elt,
                  GstBuffer *buffer,
                  GstPad *pad,
                  gpointer user_data)
{
  ((PSoundChannelGSTREAMER *) user_data)->OnWriteHandoff (elt, buffer, pad);
}


static void
cb_read_handoff (GstElement *elt,
                 GstBuffer *buffer,
                 GstPad *pad,
                 gpointer user_data)
{
  ((PSoundChannelGSTREAMER *) user_data)->OnReadHandoff (elt, buffer, pad);
}


///////////////////////////////////////////////////////////////////////////////

PSoundChannelGSTREAMER::PSoundChannelGSTREAMER()
{
  Construct();
}


PSoundChannelGSTREAMER::PSoundChannelGSTREAMER (const PString &device,
                                                Directions dir,
                                                unsigned numChannels,
                                                unsigned sampleRate,
                                                unsigned bitsPerSample)
{
  Construct();
  Open (device, dir, numChannels, sampleRate, bitsPerSample);
}


void PSoundChannelGSTREAMER::Construct()
{
  static BOOL gst_init_done = FALSE;
  int argc = 0;
  char **argv = { NULL };
  
  pipeline = NULL;


  if (!gst_init_done)
    gst_init (&argc, &argv);
  gst_init_done = TRUE;

  dataBuf = (gchar *) malloc (76800);
  memset (dataBuf, '0', 76800);
}


PSoundChannelGSTREAMER::~PSoundChannelGSTREAMER()
{
  Close();
}


PStringArray PSoundChannelGSTREAMER::GetDeviceNames (Directions dir)
{
  PStringArray devices;
 
  devices += "HAHA";

  return devices;
}


PString PSoundChannelGSTREAMER::GetDefaultDevice(Directions dir)
{
  PStringArray devicenames;
  devicenames = PSoundChannelGSTREAMER::GetDeviceNames (dir);

  return devicenames[0];
}


BOOL PSoundChannelGSTREAMER::Open (const PString & _device,
                                   Directions _dir,
                                   unsigned _numChannels,
                                   unsigned _sampleRate,
                                   unsigned _bitsPerSample)
{
  GstElement *fakesrc = NULL;
  GstElement *fakesink = NULL;
  GstElement *flt = NULL;
  GstElement *audiosink = NULL;
  GstElement *audiosrc = NULL;
  
  Close();

  mNumChannels = _numChannels;
  mSampleRate = _sampleRate;
  mBitsPerSample = _bitsPerSample;
  direction = _dir;
  isInitialised = FALSE;

  /* setup pipeline */
  pipeline = gst_pipeline_new ("pipeline");
  
  if (direction == Player) {

    fakesrc = gst_element_factory_make ("fakesrc", "source");
    flt = gst_element_factory_make ("capsfilter", "flt");
    audiosink = gst_element_factory_make ("alsasink", "audiosink");

    /* setup */
    printf ("Open with %d %d %d player\n", mBitsPerSample, mSampleRate, mNumChannels);
    g_object_set (G_OBJECT (flt), "caps",
                  gst_caps_new_simple ("audio/x-raw-int",
                                       "width", G_TYPE_INT, mBitsPerSample,
                                       "depth", G_TYPE_INT, mBitsPerSample,
                                       "signed", G_TYPE_BOOLEAN, TRUE,
                                       "rate", G_TYPE_INT, mSampleRate,
                                       "channels", G_TYPE_INT, mNumChannels, NULL), NULL);
    gst_bin_add_many (GST_BIN (pipeline), fakesrc, flt, audiosink, NULL);
    gst_element_link_many (fakesrc, flt, audiosink, NULL);
    g_signal_connect (fakesrc, "handoff", G_CALLBACK (cb_write_handoff), this);
    g_object_set (G_OBJECT (fakesrc),
                  "signal-handoffs", TRUE,
                  "sizemax", 480,
                  "sizetype", 2, NULL);

    dataBufReady = FALSE;
    gst_element_set_state (pipeline, GST_STATE_PLAYING);
  }
  else {
  
    return TRUE;
    audiosrc = gst_element_factory_make ("alsasrc", "audiosrc");
    flt = gst_element_factory_make ("capsfilter", "flt");
    fakesink = gst_element_factory_make ("fakesink", "sink");

    /* setup */
    printf ("Open with %d %d %d recorder\n", mBitsPerSample, mSampleRate, mNumChannels);
    g_object_set (G_OBJECT (flt), "caps",
                  gst_caps_new_simple ("audio/x-raw-int",
                                       "width", G_TYPE_INT, mBitsPerSample,
                                       "depth", G_TYPE_INT, mBitsPerSample,
                                       "signed", G_TYPE_BOOLEAN, TRUE,
                                       "rate", G_TYPE_INT, mSampleRate,
                                       "channels", G_TYPE_INT, mNumChannels, NULL), NULL);
    gst_bin_add_many (GST_BIN (pipeline), audiosrc, flt, fakesink, NULL);
    gst_element_link_many (audiosrc, flt, fakesink, NULL);
    g_object_set (G_OBJECT (audiosrc), "blocksize", 480, NULL);
    g_signal_connect (fakesink, "handoff", G_CALLBACK (cb_read_handoff), this);
    g_object_set (G_OBJECT (fakesink),
                  "signal-handoffs", TRUE, NULL);

    dataBufReady = TRUE;
    gst_element_set_state (pipeline, GST_STATE_PLAYING);
  }
  
  return TRUE;
}


BOOL PSoundChannelGSTREAMER::Setup()
{
  BOOL no_error = TRUE;
  
  isInitialised = TRUE;

  return no_error;
}


BOOL PSoundChannelGSTREAMER::Close()
{
  if (pipeline) {
   
    gst_element_set_state (pipeline, GST_STATE_NULL);
    g_object_unref (pipeline);
  }
  
  pipeline = NULL;

  return TRUE;
}


BOOL PSoundChannelGSTREAMER::Write (const void *buf, PINDEX len)
{
  int bufPos = 0;
  char *incomingBuffer = (char *) buf;

  while (bufPos < len) {
  
    if (dataBufReady == FALSE) {

      PWaitAndSignal m(bufferMutex);
      memset (dataBuf, '0', 480); //FIXME we hardcode 480 but we sholdn't.
      memcpy (dataBuf, (const void *) &incomingBuffer [bufPos], PMIN (480, (len - bufPos)));
      bufPos += 480;
      dataBufReady = TRUE;
    }
  }

  lastWriteCount += len;
  return TRUE;
}


void
PSoundChannelGSTREAMER::OnWriteHandoff (GstElement *elt,
                                        GstBuffer *buffer,
                                        GstPad *pad)
{
  if (dataBufReady == TRUE) {

    PWaitAndSignal m(bufferMutex);
    memcpy (GST_BUFFER_DATA (buffer), (const void *) & dataBuf [0], 480);
    memset (dataBuf, '0', 480);
    dataBufReady = FALSE;
  }
}


BOOL PSoundChannelGSTREAMER::Read (void * buf, PINDEX len)
{
  int bufPos = 0;
  char *outgoingBuffer = (char *) buf;

  while (bufPos < len) {
  
    if (dataBufReady == FALSE) {

      PWaitAndSignal m(bufferMutex);
      memset (outgoingBuffer, 0, 480);
      memcpy ((void *) &outgoingBuffer [bufPos], dataBuf, PMIN (480, (len - bufPos)));
      bufPos += 480;
      dataBufReady = TRUE;
      printf ("To Transmit (in buffer) %d\n", len);
    }
  }

  lastReadCount += len;
  
  return TRUE;
}


void
PSoundChannelGSTREAMER::OnReadHandoff (GstElement *elt,
                                       GstBuffer *buffer,
                                       GstPad *pad)
{
  if (dataBufReady == TRUE) {

    PWaitAndSignal m(bufferMutex);
    memset (dataBuf, '0', 480);
    memcpy ((void *) & dataBuf [0], GST_BUFFER_DATA (buffer), 480);
    dataBufReady = FALSE;
    printf ("Wrote Buffer Size %d\n", GST_BUFFER_SIZE (buffer));
  }
}


BOOL PSoundChannelGSTREAMER::SetFormat (unsigned numChannels,
                                   unsigned sampleRate,
                                   unsigned bitsPerSample)
{
  /* check parameters */
  PAssert((bitsPerSample == 8) || (bitsPerSample == 16), PInvalidParameter);
  PAssert(numChannels >= 1 && numChannels <= 2, PInvalidParameter);

  mNumChannels   = numChannels;
  mSampleRate    = sampleRate;
  mBitsPerSample = bitsPerSample;
 
  /* mark this channel as uninitialised */
  isInitialised = FALSE;

  return TRUE;
}


unsigned PSoundChannelGSTREAMER::GetChannels()   const
{
  return mNumChannels;
}


unsigned PSoundChannelGSTREAMER::GetSampleRate() const
{
  return mSampleRate;
}


unsigned PSoundChannelGSTREAMER::GetSampleSize() const
{
  return mBitsPerSample;
}


BOOL PSoundChannelGSTREAMER::SetBuffers (PINDEX size, PINDEX count)
{
  //storedPeriods = count;

  //storedSize = size;

  return TRUE;
}


BOOL PSoundChannelGSTREAMER::GetBuffers(PINDEX & size, PINDEX & count)
{
  //size = storedSize;
  //count = storedPeriods;
  
  return FALSE;
}


BOOL PSoundChannelGSTREAMER::PlaySound(const PSound & sound, BOOL wait)
{
  if (!Write((const BYTE *)sound, sound.GetSize()))
    return FALSE;

  printf ("End of PlaySound\n");

  if (wait)
    return WaitForPlayCompletion();

  return TRUE;
}


BOOL PSoundChannelGSTREAMER::PlayFile(const PFilePath & filename, BOOL wait)
{
  BYTE buffer [512];
  
  PFile file (filename, PFile::ReadOnly);

  if (!file.IsOpen())
    return FALSE;

  for (;;) {

    if (!file.Read (buffer, 512))
      break;

    PINDEX len = file.GetLastReadCount();
    if (len == 0)
      break;
    if (!Write(buffer, len))
      break;
  }

  file.Close();

  if (wait)
    return WaitForPlayCompletion();

  return TRUE;
}


BOOL PSoundChannelGSTREAMER::HasPlayCompleted()
{
  //FIXME

  return TRUE;
}


BOOL PSoundChannelGSTREAMER::WaitForPlayCompletion()
{
  //FIXME

  return TRUE;
}


BOOL PSoundChannelGSTREAMER::RecordSound(PSound & sound)
{
  return FALSE;
}


BOOL PSoundChannelGSTREAMER::RecordFile(const PFilePath & filename)
{
  return FALSE;
}


BOOL PSoundChannelGSTREAMER::StartRecording()
{
  return FALSE;
}


BOOL PSoundChannelGSTREAMER::IsRecordBufferFull()
{
  return TRUE;
}


BOOL PSoundChannelGSTREAMER::AreAllRecordBuffersFull()
{
  return TRUE;
}


BOOL PSoundChannelGSTREAMER::WaitForRecordBufferFull()
{
  return TRUE;
}


BOOL PSoundChannelGSTREAMER::WaitForAllRecordBuffersFull()
{
  return FALSE;
}


BOOL PSoundChannelGSTREAMER::Abort()
{
  return TRUE;
}



BOOL PSoundChannelGSTREAMER::SetVolume (unsigned newVal)
{
  return TRUE;
}


BOOL  PSoundChannelGSTREAMER::GetVolume(unsigned &devVol)
{
  return TRUE;
}
  

BOOL PSoundChannelGSTREAMER::IsOpen () const
{
  return TRUE;
}

