#define PY_SSIZE_T_CLEAN
#include <Python.h>

#include "cmunipack.h"
#include "framesetclass.h"
#include "lightcurveclass.h"

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

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

static void
LightCurve_dealloc(LightCurve* 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
LightCurve_init(LightCurve *self, PyObject *param, PyObject *kwds)
{
	int i, res, *items, flags, aperture, magnitudes = 0, jdtype = 0;
	const char *objname = NULL, *locname = NULL;
	double ra, dec, lon, lat;
	FrameSet *fset;
	PyObject *var = NULL, *comp = NULL, *check = NULL;
	PyObject *altitude = NULL, *airmass = NULL, *helcorr = NULL;
	PyObject *frameids = NULL, *object = NULL, *location = NULL;
	CmpackLCurve *lc;

	static char *kwlist[] = {"fset", "aperture", "var_list", "comp_list", "check_list", "magnitudes", 
		"altitude", "airmass", "helcorr", "jdtype", "frameids", "object", "location", NULL};
	if (!PyArg_ParseTupleAndKeywords(param, kwds, "O!iO!O!O!|iO!O!O!iO!O!O!", kwlist, &FrameSetType, &fset, 
		&aperture, &PyList_Type, &var, &PyList_Type, &comp, &PyList_Type, &check, &magnitudes, 
		&PyBool_Type, &altitude, &PyBool_Type, &airmass, &PyBool_Type, &helcorr, &jdtype, 
		&PyBool_Type, &frameids, &PyTuple_Type, &object, &PyTuple_Type, &location))
		return -1;

	if (magnitudes!=0 && magnitudes!=1) {
		PyErr_SetString(PyExc_ValueError, "Unsupported values of 'magnitudes' paremeter.");
		return -1;
	}
	if (jdtype<0 || jdtype>2) {
		PyErr_SetString(PyExc_ValueError, "Unsupported values of 'jdtype' paremeter.");
		return -1;
	}

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

	lc = cmpack_lcurve_init();
	cmpack_lcurve_set_aperture(lc, aperture);

	if (var) {
		Py_ssize_t count = PyList_Size(var);
		if (count>0) {
			items = cmpack_malloc(count*sizeof(int));
			for (i=0; i<count; i++)
				items[i] = PyLong_AsLong(PyList_GetItem(var, i));
			cmpack_lcurve_set_var(lc, items, (int)count);
			cmpack_free(items);
		}
	}
	if (comp) {
		Py_ssize_t count = PyList_Size(comp);
		if (count>0) {
			items = cmpack_malloc(count*sizeof(int));
			for (i=0; i<count; i++)
				items[i] = PyLong_AsLong(PyList_GetItem(comp, i));
			cmpack_lcurve_set_comp(lc, items, (int)count);
			cmpack_free(items);
		}
	}
	if (check) {
		Py_ssize_t count = PyList_Size(check);
		if (count>0) {
			items = cmpack_malloc(count*sizeof(int));
			for (i=0; i<count; i++)
				items[i] = PyLong_AsLong(PyList_GetItem(check, i));
			cmpack_lcurve_set_check(lc, items, (int)count);
			cmpack_free(items);
		}
	}
	if (helcorr==Py_True || airmass==Py_True || altitude==Py_True) {
		if (!object || !PyArg_ParseTuple(object, "zdd", &objname, &ra, &dec)) {
			PyErr_SetString(PyExc_ValueError, "The 'object' parameter should be in a tuple of (name, ra, dec)");
			return -1;
		}
	}
	if (airmass==Py_True || altitude==Py_True) {
		if (!location || !PyArg_ParseTuple(location, "zdd", &locname, &lon, &lat)) {
			PyErr_SetString(PyExc_ValueError, "The 'location' parameter should be in a tuple of (name, lon, lat)");
			return -1;
		}
	}

	flags = 0;
	if (magnitudes==1)
		flags |= CMPACK_LCURVE_INSTMAG;
	if (frameids==Py_True)
		flags |= CMPACK_LCURVE_FRAME_IDS;
	
	res = cmpack_lcurve(lc, fset->f, &self->t.f, flags);
	cmpack_lcurve_destroy(lc);
	if (res!=0) {
		cmpack_error(res);
		return -1;
	}

	if (helcorr==Py_True || jdtype) {
		flags = 0;
		if (helcorr!=Py_True) 
			flags |= CMPACK_HCORR_NOHELCOR;
		if (jdtype==0)
			flags |= CMPACK_HCORR_NOJULDAT;
		if (jdtype==1)
			flags |= CMPACK_HCORR_MODIFYJD;
		res = cmpack_helcorr_table(self->t.f, objname, ra, dec, NULL, flags);
		if (res!=0) {
			cmpack_error(res);
			return -1;
		}
	}

	if (airmass==Py_True || altitude==Py_True) {
		flags = 0;
		if (airmass!=Py_True)
			flags |= CMPACK_AMASS_NOAIRMASS;
		if (altitude!=Py_True)
			flags |= CMPACK_AMASS_NOALTITUDE;
		res = cmpack_airmass_table(self->t.f, objname, ra, dec, locname, lon, lat, NULL, flags);
		if (res!=0) {
			cmpack_error(res);
			return -1;
		}
	}

	return 0;
}

static PyObject *
LightCurve_aperture(LightCurve *self, void *data)
{
	int aperture;

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

	if (cmpack_tab_gkyi(self->t.f, "aperture", &aperture)==0)
		return PyLong_FromLong(aperture);
	Py_RETURN_NONE;
}

static PyObject *
LightCurve_filter(LightCurve *self, void *data)
{
	const char *filter;

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

	filter = cmpack_tab_gkys(self->t.f, "filter");
	if (filter)
		return PyUnicode_FromString(filter);
	Py_RETURN_NONE;
}

static PyObject *
LightCurve_frame_id(LightCurve *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 *
LightCurve_go_to(LightCurve *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 *
LightCurve_get_mag(Table *self, PyObject *args)
{
	const char *name;
	int j;
	double mag, err;
	PyObject *retval;

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

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

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

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

static PyObject *
LightCurve_copy(LightCurve *self, PyObject *args)
{
	int res;
	CmpackTable *newf; 

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

	newf = cmpack_tab_init(CMPACK_TABLE_LCURVE_DIFF);
	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 LightCurve_FromHandle(self->t.f);
}

static PyGetSetDef LightCurve_getset[] = {
	{"aperture", (getter)LightCurve_aperture, NULL,
	"Aperture identifier"},
	{"filter", (getter)LightCurve_filter, NULL,
	"Color filter designation"},

	{"frame_id", (getter)LightCurve_frame_id, NULL,
	"Get frame identifier from active frame"},

	{NULL}
};

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

	{"go_to", (PyCFunction)LightCurve_go_to, METH_VARARGS,
	"Activate a frame specified by its identifier"},
	{"get_mag", (PyCFunction)LightCurve_get_mag, METH_VARARGS,
	"Get magnitude and error estimation from given column"},

    {NULL}  /* Sentinel */
};

PyTypeObject LightCurveType = {
    PyObject_HEAD_INIT(NULL)
    .tp_name = "cmpack.LightCurve",          /*tp_name*/
	.tp_basicsize = sizeof(LightCurve),           /*tp_basicsize*/
    .tp_dealloc = (destructor)LightCurve_dealloc,			/*tp_dealloc*/
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /*tp_flags*/
    .tp_doc = "Light curve",			/* tp_doc */
    .tp_methods = LightCurve_methods,           /* tp_methods */
    .tp_getset = LightCurve_getset,            /* tp_getset */
    .tp_base = &TableType,                         /* tp_base */
    .tp_init = (initproc)LightCurve_init,      /* tp_init */
    .tp_new = LightCurve_new                 /* tp_new */
};

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

PyTypeObject *LightCurve_Type(void)
{
	return &LightCurveType;
}
