/*
 * Copyright (c) 2004 Jean-Yves Lefort
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of Jean-Yves Lefort nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include <Python.h>
#include <structmember.h>
#define NO_IMPORT_PYGOBJECT
#include <pygobject.h>
#include <glib/gi18n.h>
#include "streamtuner.h"
#include "pst-stream.h"
#include "pst-handler-field.h"
#include "pst-helpers.h"
#include "pst-transfer-session.h"

struct _PythonStream
{
  STStream	stream;
  PSTStream	*p;
};

/*** attributes **************************************************************/

static PyObject *pst_stream_get_name (PSTStream *self, void *closure);
static int pst_stream_set_name (PSTStream *self, PyObject *value, void *closure);

static PyGetSetDef getsetters[] = {
  { "name", (getter) pst_stream_get_name, (setter) pst_stream_set_name },
  
  { NULL }
};

/*** members *****************************************************************/

static PyMemberDef members[] = {
  { "fields", T_OBJECT, G_STRUCT_OFFSET(PSTStream, fields), READONLY },

  { NULL }
};

/*** type object *************************************************************/

static PyObject *pst_stream_new (PyTypeObject *type, PyObject *args, PyObject *keywords);
static void pst_stream_dealloc (PSTStream *self);

PyTypeObject PSTStream_Type = {
  PyObject_HEAD_INIT(NULL)
  0,				/* ob_size */
  "ST.Stream",			/* tp_name */
  sizeof(PSTStream),		/* tp_basicsize */
  0,				/* tp_itemsize */
  (destructor) pst_stream_dealloc, /* tp_dealloc */
  NULL,				/* tp_print */
  NULL,				/* tp_getattr */
  NULL,				/* tp_setattr */
  NULL,				/* tp_compare */
  NULL,				/* tp_repr */
  NULL,				/* tp_as_number */
  NULL,				/* tp_as_sequence */
  NULL,				/* tp_as_mapping */
  NULL,				/* tp_hash */
  NULL,				/* tp_call */
  NULL,				/* tp_str */
  NULL,				/* tp_getattro */
  NULL,				/* tp_setattro */
  NULL,				/* tp_as_buffer */
  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
  NULL,				/* tp_doc */
  NULL,				/* tp_traverse */
  NULL,				/* tp_clear */
  NULL,				/* tp_richcompare */
  0,				/* tp_weaklistoffset */
  NULL,				/* tp_iter */
  NULL,				/* tp_iternext */
  NULL,				/* tp_methods */
  members,			/* tp_members */
  getsetters,			/* tp_getset */
  NULL,				/* tp_base */
  NULL,				/* tp_dict */
  NULL,				/* tp_descr_get */
  NULL,				/* tp_descr_set */
  0,				/* tp_dictoffset */
  NULL,				/* tp_init */
  NULL,				/* tp_alloc */
  pst_stream_new		/* tp_new */
};

/*** function declarations ***************************************************/

static gboolean pst_stream_construct (PSTStream *self);

/*** type methods ************************************************************/

static PyObject *
pst_stream_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
{
  static char *keyword_list[] = { NULL };
  PSTStream *self;

  if (! PyArg_ParseTupleAndKeywords(args, keywords, "", keyword_list))
    return NULL;

  self = (PSTStream *) type->tp_alloc(type, 0);
  if (! self)
    return NULL;

  if (! pst_stream_construct(self))
    {
      Py_DECREF(self);
      return NULL;
    }

  return (PyObject *) self;
}

static gboolean
pst_stream_construct (PSTStream *self)
{
  self->stream = g_new0(PythonStream, 1);
  self->stream->p = self;
  self->fields = PyDict_New();

  return self->fields != NULL;
}

static void
pst_stream_dealloc (PSTStream *self)
{
  st_stream_free((STStream *) self->stream);
  Py_DECREF(self->fields);

  self->ob_type->tp_free((PyObject *) self);
}

static PyObject *
pst_stream_get_name (PSTStream *self, void *closure)
{
  return pst_string_from_string_or_null(((STStream *) self->stream)->name);
}

static int
pst_stream_set_name (PSTStream *self, PyObject *value, void *closure)
{
  return pst_string_dup_string_or_null(value, &((STStream *) self->stream)->name);
}

/*** streamtuner callbacks ***************************************************/

PythonStream *
pst_stream_new_cb (gpointer data)
{
  PSTStream *p;
  PythonStream *stream = NULL;

  PST_THREADS_ENTER

  p = PyObject_New(PSTStream, &PSTStream_Type);
  if (p)
    {
      if (pst_stream_construct(p))
	stream = p->stream;
      else
	Py_DECREF(p);
    }

  if (! stream)
    PyErr_Print();

  PST_THREADS_LEAVE

  return stream;
}

void
pst_stream_field_set_cb (PythonStream *stream,
			 STHandlerField *field,
			 const GValue *value,
			 gpointer data)
{
  PyObject *pid = NULL;
  PyObject *pvalue = NULL;

  PST_THREADS_ENTER

  pid = PyInt_FromLong(field->id);
  if (! pid)
    goto exception;

  pvalue = pyg_value_as_pyobject(value, TRUE);
  if (! pvalue)
    goto exception;

  if (PyDict_SetItem(stream->p->fields, pid, pvalue) == -1)
    goto exception;

  goto end;			/* success */

 exception:
  PyErr_Print();

 end:
  Py_XDECREF(pid);
  Py_XDECREF(pvalue);

  PST_THREADS_LEAVE
}

void
pst_stream_field_get_cb (PythonStream *stream,
			 STHandlerField *field,
			 GValue *value,
			 gpointer data)
{
  PyObject *pid;
  PyObject *pvalue;

  PST_THREADS_ENTER

  pid = PyInt_FromLong(field->id);
  if (! pid)
    goto end;

  pvalue = PyDict_GetItem(stream->p->fields, pid);
  Py_DECREF(pid);

  if (pvalue)
    pst_value_from_pyobject(value, pvalue);

 end:
  PST_THREADS_LEAVE
}

void
pst_stream_free_cb (PythonStream *stream, gpointer data)
{
  PST_THREADS_ENTER
  Py_DECREF(stream->p);
  PST_THREADS_LEAVE
}

gboolean
pst_stream_cb (PythonStream *stream, PSTCallbackInfo *info, GError **err)
{
  PyObject *presult;
  gboolean status;

  PST_THREADS_ENTER

  presult = PyObject_CallMethod(info->object, info->method, "O", stream->p);
  if (presult)
    {
      status = TRUE;
      Py_DECREF(presult);
    }
  else
    {
      pst_set_error(err);
      status = FALSE;
    }
  
  PST_THREADS_LEAVE

  return TRUE;
}

gboolean
pst_stream_tune_in_multiple_cb (GSList *streams, PSTCallbackInfo *info, GError **err)
{
  PyObject *tuple;
  PyObject *presult;
  GSList *l;
  int i = 0;
  gboolean status = FALSE;

  PST_THREADS_ENTER

  tuple = PyTuple_New(g_slist_length(streams));
  if (! tuple)
    {
      pst_set_error(err);
      goto end;
    }

  for (l = streams; l; l = l->next)
    {
      PythonStream *stream = l->data;
      
      Py_INCREF(stream->p);	/* PyTuple_SET_ITEM() steals a ref */
      PyTuple_SET_ITEM(tuple, i++, (PyObject *) stream->p);
    }
  
  presult = PyObject_CallMethod(info->object, info->method, "O", tuple);
  Py_DECREF(tuple);

  if (presult)
    {
      status = TRUE;
      Py_DECREF(presult);
    }
  else
    pst_set_error(err);
  
 end:
  PST_THREADS_LEAVE

  return status;
}

void
pst_stream_stock_field_get_cb (PythonStream *stream,
			       STHandlerStockField stock_field,
			       GValue *value,
			       PSTCallbackInfo *info)
{
  PyObject *result;

  PST_THREADS_ENTER

  result = PyObject_CallMethod(info->object, info->method, "Oi", stream->p, stock_field);
  if (result)
    {
      pst_value_from_pyobject(value, result);
      Py_DECREF(result);
    }
  else
    PyErr_Print();

  PST_THREADS_LEAVE
}

gboolean
pst_stream_modify_cb (PythonStream *stream,
		      GSList *fields,
		      GSList *values,
		      PSTCallbackInfo *info,
		      GError **err)
{
  GSList *f;
  GSList *v;
  PyObject *tuple;
  int i = 0;
  PyObject *presult;
  gboolean status = FALSE;

  PST_THREADS_ENTER

  tuple = PyTuple_New(MIN(g_slist_length(fields), g_slist_length(values)));
  if (! tuple)
    {
      pst_set_error(err);
      goto end;
    }

  for (f = fields, v = values; f && v; f = f->next, v = v->next)
    {
      PyObject *pair;

      pair = Py_BuildValue("(NN)",
			   pst_handler_field_from_field(f->data),
			   pyg_value_as_pyobject(v->data, TRUE));
      if (! pair)
	{
	  pst_set_error(err);
	  goto end;
	}

      PyTuple_SET_ITEM(tuple, i++, pair);
    }

  presult = PyObject_CallMethod(info->object, info->method,
				"OO", stream->p, tuple);

  if (presult)
    {
      Py_DECREF(presult);
      status = TRUE;
    }
  else
    pst_set_error(err);

 end:
  Py_XDECREF(tuple);

  PST_THREADS_LEAVE

  return status;
}

/*** C API *******************************************************************/

gboolean
pst_stream_register (PyObject *module)
{
  PyTypeObject *ptr = &PSTStream_Type;

  g_return_val_if_fail(module != NULL, FALSE);

  if (PyType_Ready(&PSTStream_Type) < 0)
    return FALSE;

  Py_INCREF(&PSTStream_Type);
  PyModule_AddObject(module, "Stream", (PyObject *) ptr);

  return TRUE;
}

PythonStream *
pst_stream_copy (PythonStream *stream)
{
  PSTStream *p;

  g_return_val_if_fail(stream != NULL, NULL);

  p = PyObject_New(PSTStream, &PSTStream_Type);
  if (! p)
    return NULL;

  if (! pst_stream_construct(p))
    {
      Py_DECREF(p);
      return NULL;
    }

  ((STStream *) p->stream)->name = g_strdup(((STStream *) stream)->name);
  if (PyDict_Update(p->fields, stream->p->fields) == -1)
    {
      Py_DECREF(p);
      return NULL;
    }

  return p->stream;
}
