#define PY_SSIZE_T_CLEAN
#include <Python.h>

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

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

typedef struct {
    PyObject_HEAD
	CmpackMasterDark *f;
} MasterDark;

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

static void
MasterDark_dealloc(MasterDark* self)
{
	if (self->f)
		cmpack_mdark_destroy(self->f);
    self->ob_base.ob_type->tp_free((PyObject*)self);
}

static int
MasterDark_init(MasterDark *self, PyObject *args, PyObject *kwds)
{
	int res, format = CMPACK_BITPIX_AUTO;
	double minvalue = 0.0, maxvalue = 65535.0;
	CmpackBorder b;
	PyObject *output, *border = NULL, *scalable = Py_False;

	static char *kwlist[] = {"output", "format", "border", "minvalue", "maxvalue", "scalable", 
		NULL};
    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|iO!ddO!", kwlist, &CCDFileType, &output, 
		&format, &PyTuple_Type, &border, &minvalue, &maxvalue, &PyBool_Type, &scalable))
        return -1;

	if (!self->f) {
		self->f = cmpack_mdark_init();
		if (!self->f) {
			PyErr_SetString(PyExc_MemoryError, "Memory allocation error");
			return -1;
		}
	}
	res = cmpack_mdark_open(self->f, ((CCDFile*)output)->f);
	if (res!=0) {
		cmpack_error(res);
		return -1;
	}
	cmpack_mdark_set_bitpix(self->f, format);
	if (border) {
		if (!PyArg_ParseTuple(border, "iiii", &b.left, &b.top, &b.right, &b.bottom)) 
			return -1;
		cmpack_mdark_set_border(self->f, &b);
	}
	cmpack_mdark_set_minvalue(self->f, minvalue);
	cmpack_mdark_set_maxvalue(self->f, maxvalue);
	cmpack_mdark_set_scalable(self->f, scalable == Py_True);
	return 0;
}

static PyObject *
MasterDark_flush(MasterDark *self, PyObject *args)
{
	if (self->f) {
		int res = cmpack_mdark_close(self->f);
		self->f = NULL;
		if (res!=0)
			return cmpack_error(res);
	}
	Py_RETURN_NONE;
}

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

static PyObject *
MasterDark_format(MasterDark *self, void *data)
{
	return PyLong_FromLong(cmpack_mdark_get_bitpix(self->f));
}

static PyObject *
MasterDark_scalable(MasterDark *self, void *data)
{
	return PyBool_FromLong(cmpack_mdark_get_scalable(self->f));
}

static PyObject *
MasterDark_minvalue(MasterDark *self, void *data)
{
	return PyFloat_FromDouble(cmpack_mdark_get_minvalue(self->f));
}

static PyObject *
MasterDark_maxvalue(MasterDark *self, void *data)
{
	return PyFloat_FromDouble(cmpack_mdark_get_maxvalue(self->f));
}

static PyObject *
MasterDark_add(MasterDark *self, PyObject *param)
{
	int res;

	if (!CCDFile_Check(param)) {
		/* Other types */
		PyErr_SetString(PyExc_TypeError, "Frame should be a CCDFile object.");
		return NULL;
	}

	res = cmpack_mdark_read(self->f, ((CCDFile*)param)->f);
	if (res!=0) {
		cmpack_error(res);
		return NULL;
	}
	Py_RETURN_NONE;
}

static PyGetSetDef MasterDark_getset[] = {
	{"format", (getter)MasterDark_format, NULL, 
	"Image border size in pixels"},
	{"scalable", (getter)MasterDark_scalable, NULL, 
	"Make scalable dark frame?"},
	{"border", (getter)MasterDark_border, NULL, 
	"Image border size in pixels"},
	{"minvalue", (getter)MasterDark_minvalue, NULL, 
	"Threshold for \"invalid\" pixel values"},
	{"maxvalue", (getter)MasterDark_maxvalue, NULL, 
	"Threshold for \"overexposed\" pixels"},
	{NULL}
};

static PyMethodDef MasterDark_methods[] = {
	{"flush", (PyCFunction)MasterDark_flush, METH_NOARGS,
	"Close file"},
	{"add", (PyCFunction)MasterDark_add, METH_O,
	"Add a frame"},
    {NULL}  /* Sentinel */
};

PyTypeObject MasterDarkType = {
    PyObject_HEAD_INIT(NULL)
    .tp_new = "cmpack.MasterDark",          /*tp_name*/
    .tp_basicsize = sizeof(MasterDark),           /*tp_basicsize*/
    .tp_dealloc = (destructor)MasterDark_dealloc,           /*tp_dealloc*/
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /*tp_flags*/
    .tp_doc = "MasterDark correction",        /* tp_doc */
    .tp_methods = MasterDark_methods,           /* tp_methods */
    .tp_getset = MasterDark_getset,            /* tp_getset */
    .tp_init = (initproc)MasterDark_init,      /* tp_init */
    .tp_new = MasterDark_new                 /* tp_new */
};
