/* Farstream generic unit tests for transmitters
 *
 * Copyright (C) 2007 Collabora, Nokia
 * @author: Olivier Crete <olivier.crete@collabora.co.uk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <gst/check/gstcheck.h>
#include <farstream/fs-transmitter.h>
#include <farstream/fs-stream-transmitter.h>

#include <sys/types.h>
#include <sys/wait.h>

#include "check-threadsafe.h"
#include "generic.h"

static void
_transmitter_error (FsTransmitter *transmitter, gint errorno, gchar *error_msg,
    gpointer user_data)
{
  ts_fail ("Transmitter(%x) error(%d) msg:%s debug:%s", transmitter, errorno,
    error_msg);
}

void
stream_transmitter_error (FsStreamTransmitter *streamtransmitter,
  gint errorno, gchar *error_msg, gpointer user_data)
{
  ts_fail ("StreamTransmitter(%x) error(%d) msg:%s", streamtransmitter,
    errorno, error_msg);
}

void
setup_fakesrc (FsTransmitter *trans, GstElement *pipeline, guint component_id)
{
  GstElement *src;
  GstElement *trans_sink;
  gchar *padname;
  gchar *tmp;

  tmp = g_strdup_printf ("fakemediasrc_%u", component_id);
  src = gst_element_factory_make ("fakesrc", tmp);
  g_free (tmp);
  g_object_set (src,
      "num-buffers", 20,
      "sizetype", 2,
      "sizemax", component_id * 10,
      "is-live", TRUE,
      "filltype", 2,
      NULL);

  /*
   * We lock and unlock the state to prevent the source to start
   * playing before we link it
   */
  gst_element_set_locked_state (src, TRUE);

  ts_fail_unless (gst_bin_add (GST_BIN (pipeline), src),
    "Could not add the fakesrc");

  g_object_get (trans, "gst-sink", &trans_sink, NULL);

  padname = g_strdup_printf ("sink_%u", component_id);
  ts_fail_unless (gst_element_link_pads (src, "src", trans_sink, padname),
    "Could not link the fakesrc to %s", padname);
  g_free (padname);

  gst_element_set_locked_state (src, FALSE);

  ts_fail_if (gst_element_set_state (src, GST_STATE_PLAYING) ==
    GST_STATE_CHANGE_FAILURE, "Could not set the fakesrc to playing");

  gst_element_sync_state_with_parent (src);

  gst_object_unref (trans_sink);
}

GstElement *
setup_pipeline (FsTransmitter *trans, GCallback cb)
{
  GstElement *pipeline;
  GstElement *rtpfakesink, *rtcpfakesink;
  GstElement *trans_sink, *trans_src;

  ts_fail_unless (g_signal_connect (trans, "error",
      G_CALLBACK (_transmitter_error), NULL), "Could not connect signal");

  pipeline = gst_pipeline_new ("pipeline");
  rtpfakesink = gst_element_factory_make ("fakesink", "rtpfakesink");
  rtcpfakesink = gst_element_factory_make ("fakesink", "rtcpfakesink");

  g_object_get (trans, "gst-sink", &trans_sink, "gst-src", &trans_src, NULL);


  g_object_set (rtpfakesink, "signal-handoffs", TRUE, "sync", FALSE, "async", FALSE, NULL);
  g_object_set (rtcpfakesink, "signal-handoffs", TRUE, "sync", FALSE,
    "async", FALSE, NULL);

  if (cb) {
    g_signal_connect (rtpfakesink, "handoff", cb, GINT_TO_POINTER (1));
    g_signal_connect (rtcpfakesink, "handoff", cb, GINT_TO_POINTER (2));
  }

  ts_fail_if (trans_sink == NULL, "No transmitter sink");
  ts_fail_if (trans_src == NULL, "No transmitter src");

  gst_bin_add_many (GST_BIN (pipeline), rtpfakesink, rtcpfakesink,
    trans_sink, trans_src, NULL);

  ts_fail_unless (gst_element_link_pads (trans_src, "src_1",
      rtpfakesink, "sink"),
    "Coult not link transmitter src and fakesink");
  ts_fail_unless (gst_element_link_pads (trans_src, "src_2",
      rtcpfakesink, "sink"),
    "Coult not link transmitter src and fakesink");

  g_object_unref (trans_src);
  g_object_unref (trans_sink);

  return pipeline;
}


gboolean
bus_error_callback (GstBus *bus, GstMessage *message, gpointer user_data)
{
  switch (GST_MESSAGE_TYPE (message))
  {
    case GST_MESSAGE_ERROR:
      {
        GError *error = NULL;
        gchar *debug = NULL;
        gst_message_parse_error (message, &error, &debug);

        ts_fail ("Got an error on the BUS (%d): %s (%s)", error->code,
            error->message, debug);
        g_error_free (error);
        g_free (debug);
      }
      break;
    case GST_MESSAGE_WARNING:
      {
        GError *error = NULL;
        gchar *debug = NULL;
        gst_message_parse_warning (message, &error, &debug);

        GST_WARNING ("Got a warning on the BUS (%d): %s (%s)",
            error->code,
            error->message, debug);
        g_error_free (error);
        g_free (debug);
      }
      break;
    default:
      break;
  }

  return TRUE;
}

void
test_transmitter_creation (gchar *transmitter_name)
{
  GError *error = NULL;
  FsTransmitter *trans;
  GstElement *pipeline;
  GstElement *trans_sink, *trans_src;

  trans = fs_transmitter_new (transmitter_name, 2, 0, &error);

  if (error) {
    ts_fail ("Error creating transmitter: (%s:%d) %s",
      g_quark_to_string (error->domain), error->code, error->message);
  }

  ts_fail_if (trans == NULL, "No transmitter create, yet error is still NULL");

  pipeline = setup_pipeline (trans, NULL);

  g_object_get (trans, "gst-sink", &trans_sink, "gst-src", &trans_src, NULL);

  ts_fail_if (trans_sink == NULL, "Sink is NULL");
  ts_fail_if (trans_src == NULL, "Src is NULL");

  gst_object_unref (trans_sink);
  gst_object_unref (trans_src);

  g_object_unref (trans);

  gst_object_unref (pipeline);

}


GPid stund_pid = 0;

void
setup_stund (void)
{
  GError *error = NULL;
  gchar *argv[] = {"stund", NULL};

  stund_pid = 0;

  if (!g_spawn_async (NULL, argv, NULL,
          G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
          NULL, NULL, &stund_pid, &error))
  {
    GST_WARNING ("Could not spawn stund, skipping stun testing: %s",
        error->message);
    g_clear_error (&error);
    return;
  }
}

void
teardown_stund (void)
{
  if (!stund_pid)
    return;

  kill (stund_pid, SIGTERM);
  waitpid (stund_pid, NULL, 0);
  g_spawn_close_pid (stund_pid);
  stund_pid = 0;
}

