/*
 * 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>
#define NO_IMPORT_PYGOBJECT
#include <pygobject.h>
#include <glib/gi18n.h>
#include "streamtuner.h"
#include "pst-handler-config.h"
#include "pst-helpers.h"

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

static int pst_handler_config_length (PSTHandlerConfig *self);
static PyObject *pst_handler_config_subscript (PSTHandlerConfig *self, PyObject *key);
static int pst_handler_config_ass_subscript (PSTHandlerConfig *self,
					     PyObject *key,
					     PyObject *value);

static PyMappingMethods mapping_methods = {
  (inquiry) pst_handler_config_length,
  (binaryfunc) pst_handler_config_subscript,
  (objobjargproc) pst_handler_config_ass_subscript
};

/*** methods *****************************************************************/

static PyObject *pst_handler_config_has_key (PSTHandlerConfig *self, PyObject *args);
static PyObject *pst_handler_config_keys (PSTHandlerConfig *self, PyObject *args);
static PyObject *pst_handler_config_items (PSTHandlerConfig *self, PyObject *args);

static PyObject *pst_handler_config_register_method (PSTHandlerConfig *self, PyObject *args);
static PyObject *pst_handler_config_lookup (PSTHandlerConfig *self, PyObject *args);

static PyMethodDef methods[] = {
  /* standard dictionary methods */
  { "has_key", (PyCFunction) pst_handler_config_has_key, METH_VARARGS },
  { "keys", (PyCFunction) pst_handler_config_keys, METH_NOARGS },
  { "items", (PyCFunction) pst_handler_config_items, METH_NOARGS },

  /* specific ST.HandlerConfig methods */
  { "register", (PyCFunction) pst_handler_config_register_method, METH_VARARGS },
  { "lookup", (PyCFunction) pst_handler_config_lookup, METH_VARARGS },
  
  { NULL }
};

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

PyTypeObject PSTHandlerConfig_Type = {
  PyObject_HEAD_INIT(NULL)
  0,				/* ob_size */
  "ST.HandlerConfig",		/* tp_name */
  sizeof(PSTHandlerConfig),	/* tp_basicsize */
  0,				/* tp_itemsize */
  NULL,				/* 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 */
  &mapping_methods,		/* 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 */
  methods,			/* tp_methods */
  NULL,				/* tp_members */
  NULL,				/* 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 */
  NULL				/* tp_new */
};

/*** type definitions ********************************************************/

typedef struct
{
  gboolean	status;
  int		len;
  PyObject	**tuple;
} TupleInfo;

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

static void pst_handler_config_length_cb (GParamSpec *pspec,
					  const GValue *value,
					  gpointer data);
static void pst_handler_config_keys_cb (GParamSpec *pspec,
					const GValue *value,
					gpointer data);
static void pst_handler_config_items_cb (GParamSpec *pspec,
					 const GValue *value,
					 gpointer data);


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

static int
pst_handler_config_length (PSTHandlerConfig *self)
{
  int len = 0;

  st_handler_config_foreach(self->handler, pst_handler_config_length_cb, &len);

  return len;
}

static void
pst_handler_config_length_cb (GParamSpec *pspec,
			      const GValue *value,
			      gpointer data)
{
  int *len = data;
  
  *len++;
}

static PyObject *
pst_handler_config_subscript (PSTHandlerConfig *self, PyObject *key)
{
  const char *ckey;
  GValue value = { 0, };
  PyObject *result;

  ckey = PyString_AsString(key);
  if (! ckey)
    return NULL;

  if (! st_handler_config_lookup(self->handler, ckey))
    {
      PyErr_SetObject(PyExc_KeyError, key);
      return NULL;
    }

  st_handler_config_get_value(self->handler, ckey, &value);
  result = pyg_value_as_pyobject(&value, TRUE);
  g_value_unset(&value);

  return result;
}

static int
pst_handler_config_ass_subscript (PSTHandlerConfig *self,
				  PyObject *key,
				  PyObject *value)
{
  const char *ckey;
  GParamSpec *pspec;
  GValue gvalue = { 0, };
  int result;

  if (! value)
    {
      PyErr_SetString(PyExc_TypeError, _("configuration keys cannot be removed"));
      return -1;
    }

  ckey = PyString_AsString(key);
  if (! ckey)
    return -1;

  pspec = st_handler_config_lookup(self->handler, ckey);
  if (! pspec)
    {
      PyErr_SetObject(PyExc_KeyError, key);
      return -1;
    }

  g_value_init(&gvalue, G_PARAM_SPEC_VALUE_TYPE(pspec));
  if (pst_value_from_pyobject(&gvalue, value) == 0)
    {
      st_handler_config_set_value(self->handler, ckey, &gvalue);
      result = 0;		/* success */
    }
  else
    {
      PyErr_SetString(PyExc_TypeError, _("invalid value type for this configuration key"));
      result = -1;
    }
  g_value_unset(&gvalue);

  return result;
}

/*** object methods **********************************************************/

static PyObject *
pst_handler_config_has_key (PSTHandlerConfig *self, PyObject *args)
{
  const char *key;

  if (! PyArg_ParseTuple(args, "s", &key))
    return NULL;

  return PyBool_FromLong(st_handler_config_lookup(self->handler, key) != NULL);
}

static PyObject *
pst_handler_config_keys (PSTHandlerConfig *self, PyObject *args)
{
  PyObject *tuple;
  TupleInfo info;

  tuple = PyTuple_New(0);
  if (! tuple)
    return NULL;

  info.status = TRUE;
  info.len = 0;
  info.tuple = &tuple;

  st_handler_config_foreach(self->handler, pst_handler_config_keys_cb, &info);
  
  if (! info.status)
    {
      if (tuple)
	{
	  Py_DECREF(tuple);
	  tuple = NULL;
	}
    }

  return tuple;
}

static void
pst_handler_config_keys_cb (GParamSpec *pspec,
			    const GValue *value,
			    gpointer data)
{
  TupleInfo *info = data;
  PyObject *pkey;

  if (! info->status)
    return;

  pkey = PyString_FromString(g_param_spec_get_name(pspec));
  if (! pkey)
    goto exception;
  
  if (_PyTuple_Resize(info->tuple, ++info->len) != 0)
    goto exception;
  
  if (PyTuple_SetItem(*info->tuple, info->len - 1, pkey) == 0)
    return;			/* success */

 exception:
  info->status = FALSE;
  Py_XDECREF(pkey);
}

static PyObject *
pst_handler_config_items (PSTHandlerConfig *self, PyObject *args)
{
  PyObject *tuple;
  TupleInfo info;

  tuple = PyTuple_New(0);
  if (! tuple)
    return NULL;

  info.status = TRUE;
  info.len = 0;
  info.tuple = &tuple;

  st_handler_config_foreach(self->handler, pst_handler_config_items_cb, &info);
  
  if (! info.status)
    {
      if (tuple)
	{
	  Py_DECREF(tuple);
	  tuple = NULL;
	}
    }

  return tuple;
}

static void
pst_handler_config_items_cb (GParamSpec *pspec,
			     const GValue *value,
			     gpointer data)
{
  TupleInfo *info = data;
  PyObject *pair;

  if (! info->status)
    return;

  pair = Py_BuildValue("(sN)",
		       g_param_spec_get_name(pspec),
		       pyg_value_as_pyobject(value, TRUE));
  if (! pair)
    goto exception;

  if (_PyTuple_Resize(info->tuple, ++info->len) != 0)
    goto exception;
  
  if (PyTuple_SetItem(*info->tuple, info->len - 1, pair) == 0)
    return;			/* success */

 exception:
  info->status = FALSE;
  Py_XDECREF(pair);
}

static PyObject *
pst_handler_config_register_method (PSTHandlerConfig *self, PyObject *args)
{
  PyObject *ptuple;
  GParamSpec *pspec;

  if (! PyArg_ParseTuple(args, "O!", &PyTuple_Type, &ptuple))
    return NULL;

  pspec = pyg_param_spec_from_object(ptuple);
  if (! pspec)
    return NULL;

  st_handler_config_register(self->handler, pspec);

  return pst_none();
}

static PyObject *
pst_handler_config_lookup (PSTHandlerConfig *self, PyObject *args)
{
  const char *key;
  GParamSpec *pspec;

  if (! PyArg_ParseTuple(args, "s", &key))
    return NULL;

  pspec = st_handler_config_lookup(self->handler, key);

  return pspec ? pyg_param_spec_new(pspec) : pst_none();
}

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

gboolean
pst_handler_config_register (PyObject *module)
{
  PyTypeObject *ptr = &PSTHandlerConfig_Type;

  g_return_val_if_fail(module != NULL, FALSE);

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

  Py_INCREF(&PSTHandlerConfig_Type);
  PyModule_AddObject(module, "HandlerConfig", (PyObject *) ptr);
  
  return TRUE;
}

PyObject *
pst_handler_config_new (STHandler *handler)
{
  PSTHandlerConfig *self;

  self = PyObject_New(PSTHandlerConfig, &PSTHandlerConfig_Type);
  if (! self)
    return NULL;

  self->handler = handler;

  return (PyObject *) self;
}
