#define PY_SSIZE_T_CLEAN
#include <Python.h>

#include "cmunipack.h"
#include "ccdfileclass.h"
#include "phtfileclass.h"

PyObject *cmpack_error(int code);

typedef struct {
    PyObject_HEAD
	CmpackPhot *f;
} Photometry;

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

static void
Photometry_dealloc(Photometry* self)
{
	if (self->f)
		cmpack_phot_destroy(self->f);
    self->ob_base.ob_type->tp_free((PyObject*)self);
}

static int
Photometry_init(Photometry *self, PyObject *args)
{
	if (!self->f) {
		self->f = cmpack_phot_init();
		if (!self->f) {
			PyErr_SetString(PyExc_MemoryError, "Memory allocation error");
			return -1;
		}
	}
	return 0;
}

static PyObject *
Photometry_border(Photometry *self, void *data)
{
	CmpackBorder b;
	cmpack_phot_get_border(self->f, &b);
	return Py_BuildValue("iiii", b.left, b.top, b.right, b.bottom);
}

static int
Photometry_set_border(Photometry *self, PyObject *value, void *data)
{
	CmpackBorder b;

	if (!PyArg_ParseTuple(value, "iiii", &b.left, &b.top, &b.right, &b.bottom))
		return -1;

	cmpack_phot_set_border(self->f, &b);
	return 0;
}

static PyObject *
Photometry_minvalue(Photometry *self, void *data)
{
	return PyFloat_FromDouble(cmpack_phot_get_minval(self->f));
}

static int
Photometry_set_minvalue(Photometry *self, PyObject *value, void *data)
{
	cmpack_phot_set_minval(self->f, PyFloat_AsDouble(value));
	return 0;
}

static PyObject *
Photometry_maxvalue(Photometry *self, void *data)
{
	return PyFloat_FromDouble(cmpack_phot_get_maxval(self->f));
}

static int
Photometry_set_maxvalue(Photometry *self, PyObject *value, void *data)
{
	cmpack_phot_set_maxval(self->f, PyFloat_AsDouble(value));
	return 0;
}

static PyObject *
Photometry_rnoise(Photometry *self, void *data)
{
	return PyFloat_FromDouble(cmpack_phot_get_rnoise(self->f));
}

static int
Photometry_set_rnoise(Photometry *self, PyObject *value, void *data)
{
	cmpack_phot_set_rnoise(self->f, PyFloat_AsDouble(value));
	return 0;
}

static PyObject *
Photometry_adcgain(Photometry *self, void *data)
{
	return PyFloat_FromDouble(cmpack_phot_get_adcgain(self->f));
}

static int
Photometry_set_adcgain(Photometry *self, PyObject *value, void *data)
{
	cmpack_phot_set_adcgain(self->f, PyFloat_AsDouble(value));
	return 0;
}

static PyObject *
Photometry_fwhm(Photometry *self, void *data)
{
	return PyFloat_FromDouble(cmpack_phot_get_fwhm(self->f));
}

static int
Photometry_set_fwhm(Photometry *self, PyObject *value, void *data)
{
	cmpack_phot_set_fwhm(self->f, PyFloat_AsDouble(value));
	return 0;
}

static PyObject *
Photometry_threshold(Photometry *self, void *data)
{
	return PyFloat_FromDouble(cmpack_phot_get_thresh(self->f));
}

static int
Photometry_set_threshold(Photometry *self, PyObject *value, void *data)
{
	cmpack_phot_set_thresh(self->f, PyFloat_AsDouble(value));
	return 0;
}

static PyObject *
Photometry_minsharpness(Photometry *self, void *data)
{
	return PyFloat_FromDouble(cmpack_phot_get_minshrp(self->f));
}

static int
Photometry_set_minsharpness(Photometry *self, PyObject *value, void *data)
{
	cmpack_phot_set_minshrp(self->f, PyFloat_AsDouble(value));
	return 0;
}

static PyObject *
Photometry_maxsharpness(Photometry *self, void *data)
{
	return PyFloat_FromDouble(cmpack_phot_get_maxshrp(self->f));
}

static int
Photometry_set_maxsharpness(Photometry *self, PyObject *value, void *data)
{
	cmpack_phot_set_maxshrp(self->f, PyFloat_AsDouble(value));
	return 0;
}

static PyObject *
Photometry_minroundness(Photometry *self, void *data)
{
	return PyFloat_FromDouble(cmpack_phot_get_minrnd(self->f));
}

static int
Photometry_set_minroundness(Photometry *self, PyObject *value, void *data)
{
	cmpack_phot_set_minrnd(self->f, PyFloat_AsDouble(value));
	return 0;
}

static PyObject *
Photometry_maxroundness(Photometry *self, void *data)
{
	return PyFloat_FromDouble(cmpack_phot_get_maxrnd(self->f));
}

static int
Photometry_set_maxroundness(Photometry *self, PyObject *value, void *data)
{
	cmpack_phot_set_maxrnd(self->f, PyFloat_AsDouble(value));
	return 0;
}

static PyObject *
Photometry_skyannulus(Photometry *self, void *data)
{
	return Py_BuildValue("dd", cmpack_phot_get_skyin(self->f), 
		cmpack_phot_get_skyout(self->f));
}

static int
Photometry_set_skyannulus(Photometry *self, PyObject *value, void *data)
{
	double skyin, skyout;

	if (!PyArg_ParseTuple(value, "dd", &skyin, &skyout))
		return -1;

	cmpack_phot_set_skyin(self->f, skyin);
	cmpack_phot_set_skyout(self->f, skyout);
	return 0;
}

static PyObject *
Photometry_apertures(Photometry *self, void *data)
{
	int i, count;
	double *items;
	PyObject *res;

	cmpack_phot_get_aper(self->f, &items, &count);
	res = PyList_New(count);
	for (i=0; i<count; i++)
		PyList_Append(res, PyFloat_FromDouble(items[i]));
	cmpack_free(items);
	return res;
}

static int
Photometry_set_apertures(Photometry *self, PyObject *value, void *data)
{
	int i;
	double *items;

	if (!PyList_Check(value))
		return -1;
	Py_ssize_t count = PyList_Size(value);
	if (count>0) {
		items = cmpack_malloc(count*sizeof(double));
		for (i=0; i<count; i++)
			items[i] = PyFloat_AsDouble(PyList_GetItem(value, i));
		cmpack_phot_set_aper(self->f, items, (int)count);
		cmpack_free(items);
	} else {
		cmpack_phot_set_aper(self->f, 0, 0);
	}
	return 0;
}

static PyObject *
Photometry_run(Photometry *self, PyObject *param)
{
	int res;
	CCDFile *file;
	CmpackPhtFile *dst;

	if (!PyArg_ParseTuple(param, "O!", &CCDFileType, &file)) 
		return NULL;

	dst = cmpack_pht_init();
	res = cmpack_phot(self->f, file->f, dst, NULL);
	if (res!=0) {
		cmpack_error(res);
		cmpack_pht_destroy(dst);
		return NULL;
	}
	return PhtFile_FromHandle(dst);
}

static PyGetSetDef Photometry_getset[] = {
	{"border", (getter)Photometry_border, (setter)Photometry_set_border, 
	"Image border size in pixels"},
	{"minvalue", (getter)Photometry_minvalue, (setter)Photometry_set_minvalue, 
	"Threshold for \"invalid\" pixel values"},
	{"maxvalue", (getter)Photometry_maxvalue, (setter)Photometry_set_maxvalue, 
	"Threshold for \"overexposed\" pixels"},
	{"rnoise", (getter)Photometry_rnoise, (setter)Photometry_set_rnoise,
	"Readout noise level"},
	{"adcgain", (getter)Photometry_adcgain, (setter)Photometry_set_adcgain,
	"A/D converter gain"},
	{"fwhm", (getter)Photometry_fwhm, (setter)Photometry_set_fwhm,
	"Expected value of FWHM of objects"},
	{"threshold", (getter)Photometry_threshold, (setter)Photometry_set_threshold,
	"Detection threshold"},
	{"minsharpness", (getter)Photometry_minsharpness, (setter)Photometry_set_minsharpness,
	"Min. sharpness threshold"},
	{"maxsharpness", (getter)Photometry_maxsharpness, (setter)Photometry_set_maxsharpness,
	"Max. sharpness threshold"},
	{"minroundness", (getter)Photometry_minroundness, (setter)Photometry_set_minroundness,
	"Min. roundness threshold"},
	{"maxroundness", (getter)Photometry_maxroundness, (setter)Photometry_set_maxroundness,
	"Max. roundness threshold"},
	{"skyannulus", (getter)Photometry_skyannulus, (setter)Photometry_set_skyannulus,
	"Sky inner and outer radii"},
	{"apertures", (getter)Photometry_apertures, (setter)Photometry_set_apertures,
	"List of apertures"},
	{NULL}
};

static PyMethodDef Photometry_methods[] = {
	{"run", (PyCFunction)Photometry_run, METH_VARARGS,
	"Execute photometry, returns a new photometry file"},
    {NULL}  /* Sentinel */
};

PyTypeObject PhotometryType = {
    PyObject_HEAD_INIT(NULL)
    .tp_new = "cmpack.Photometry",          /*tp_name*/
    .tp_basicsize = sizeof(Photometry),           /*tp_basicsize*/
    .tp_dealloc = (destructor)Photometry_dealloc,           /*tp_dealloc*/
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /*tp_flags*/
    .tp_doc = "Photometry",			/* tp_doc */
    .tp_methods = Photometry_methods,           /* tp_methods */
    .tp_getset = Photometry_getset,            /* tp_getset */
    .tp_init = (initproc)Photometry_init,      /* tp_init */
    .tp_new = Photometry_new                 /* tp_new */
};
