#define PY_SSIZE_T_CLEAN
#include <Python.h>

#include "cmunipack.h"
#include "framesetclass.h"
#include "trackcurveclass.h"

PyObject *cmpack_error(int code);
PyObject *err_closed(void);

static PyObject *
TrackCurve_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    TrackCurve *self = (TrackCurve*)type->tp_alloc(type, 0);
    if (self != NULL) 
		self->t.f = NULL;
    return (PyObject *)self;
}

static void
TrackCurve_dealloc(TrackCurve* self)
{
	if (self->t.f) {
		cmpack_tab_destroy(self->t.f);
		self->t.f = NULL;
	}
    self->t.ob_base.ob_type->tp_free((PyObject*)self);
}

static int
TrackCurve_init(TrackCurve *self, PyObject *param, PyObject *kwds)
{
	int res, flags;
	FrameSet *fset;
	PyObject *frameids = NULL;

	static char *kwlist[] = {"fset", "frameids", NULL};
	if (!PyArg_ParseTupleAndKeywords(param, kwds, "O!|i", kwlist, &FrameSetType, &fset, 
		&PyBool_Type, &frameids))
		return -1;

	if (self->t.f) {
		cmpack_tab_destroy(self->t.f);
		self->t.f = NULL;
	}

	flags = 0;
	if (frameids==Py_True)
		flags |= CMPACK_TCURVE_FRAME_IDS;
	
	res = cmpack_tcurve(fset->f, &self->t.f, flags, NULL);
	if (res!=0) {
		cmpack_error(res);
		return -1;
	}

	return 0;
}

static PyObject *
TrackCurve_frame_id(TrackCurve *self, void *data)
{
	int frame_id, column;

	if (!self->t.f)
		return err_closed();

	column = cmpack_tab_find_column(self->t.f, "FRAME");
	if (column<0)
		Py_RETURN_NONE;

	if (cmpack_tab_gtdi(self->t.f, column, &frame_id)!=0)
		Py_RETURN_NONE;

	return PyLong_FromLong(frame_id);
}

static PyObject *
TrackCurve_go_to(TrackCurve *self, PyObject *args)
{
	int f, frame_id, res, column;

	if (!self->t.f)
		return err_closed();

	if (!PyArg_ParseTuple(args, "i", &frame_id))
		return NULL;

	column = cmpack_tab_find_column(self->t.f, "FRAME");
	if (column<0)
		Py_RETURN_FALSE;

	res = cmpack_tab_rewind(self->t.f);
	while (res==0) {
		if (cmpack_tab_gtdi(self->t.f, column, &f)==0 && frame_id==f)
			Py_RETURN_TRUE;
		res = cmpack_tab_next(self->t.f);
	}
	Py_RETURN_FALSE;
}

static PyObject *
TrackCurve_offset(Table *self, void *data)
{
	int j;
	double x, y;
	PyObject *retval;

	if (!self->f)
		return err_closed();

	if (cmpack_tab_eof(self->f))
		Py_RETURN_NONE;

	j = cmpack_tab_find_column(self->f, "OFFSETX");
	if (j<0)
		Py_RETURN_NONE;
	
	retval = PyTuple_New(2);
	if (cmpack_tab_gtdd(self->f, j, &x)==0)
		PyTuple_SET_ITEM(retval, 0, PyFloat_FromDouble(x));
	else {
		Py_INCREF(Py_None);
		PyTuple_SET_ITEM(retval, 0, Py_None);
	}
	if (cmpack_tab_gtdd(self->f, j+1, &y)==0)
		PyTuple_SET_ITEM(retval, 1, PyFloat_FromDouble(y));
	else {
		Py_INCREF(Py_None);
		PyTuple_SET_ITEM(retval, 1, Py_None);
	}
	return retval;
}

static PyObject *
TrackCurve_copy(TrackCurve *self, PyObject *args)
{
	int res;
	CmpackTable *newf; 

	if (!self->t.f)
		return err_closed();

	newf = cmpack_tab_init(CMPACK_TABLE_TRACKLIST);
	if (!newf) {
		PyErr_SetString(PyExc_MemoryError, "Memory allocation error.");
		return NULL;
	}
	res = cmpack_tab_copy(newf, self->t.f);
	if (res!=0) {
		cmpack_tab_destroy(newf);
		return cmpack_error(res);
	}
	return TrackCurve_FromHandle(self->t.f);
}

static PyGetSetDef TrackCurve_getset[] = {
	{"frame_id", (getter)TrackCurve_frame_id, NULL,
	"Get frame identifier from active frame"},
	{"offset", (getter)TrackCurve_offset, NULL,
	"Get magnitude and error estimation from given column"},

	{NULL}
};

static PyMethodDef TrackCurve_methods[] = {
	{"copy", (PyCFunction)TrackCurve_copy, METH_NOARGS,
	"Make copy of the frame set"},

	{"go_to", (PyCFunction)TrackCurve_go_to, METH_VARARGS,
	"Activate a frame specified by its identifier"},

    {NULL}  /* Sentinel */
};

PyTypeObject TrackCurveType = {
    PyObject_HEAD_INIT(NULL)
    .tp_name = "cmpack.TrackCurve",          /*tp_name*/
	.tp_basicsize = sizeof(TrackCurve),           /*tp_basicsize*/
    .tp_dealloc = (destructor)TrackCurve_dealloc,			/*tp_dealloc*/
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /*tp_flags*/
    .tp_doc = "Track curve",			/* tp_doc */
    .tp_methods = TrackCurve_methods,           /* tp_methods */
    .tp_getset = TrackCurve_getset,            /* tp_getset */
    .tp_base = &TableType,                         /* tp_base */
    .tp_init = (initproc)TrackCurve_init,      /* tp_init */
    .tp_new = TrackCurve_new                 /* tp_new */
};

PyObject *
TrackCurve_FromHandle(CmpackTable *f)
{
	Table *pDoc = PyObject_New(Table, &TrackCurveType);
	if (pDoc) {
		pDoc->f = f;
	}
	return (PyObject*)pDoc;
}

PyTypeObject *TrackCurve_Type(void)
{
	return &TrackCurveType;
}
