#define PY_SSIZE_T_CLEAN
#include <Python.h>

#include "cmunipack.h"
#include "phtfileclass.h"
#include "catfileclass.h"

PyObject *cmpack_error(int code);

typedef struct {
    PyObject_HEAD
	CmpackMatch *f;
} Match;

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

static void
Match_dealloc(Match* self)
{
	if (self->f)
		cmpack_match_destroy(self->f);
    self->ob_base.ob_type->tp_free((PyObject*)self);
}

static int
Match_init(Match *self, PyObject *args, PyObject *kwds)
{
	int res, readstars = 10, identstars = 5, method = CMPACK_MATCH_STANDARD;
	double clipping = 2.5, maxoffset = 5;
	PyObject *file;

	static char *kwlist[] = {"file", "readstars", "identstars", "clipping", "method", 
		"maxoffset", NULL};
	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iidid", kwlist, &file, &readstars, 
		&identstars, &clipping, &method, &maxoffset)) 
		return -1;

	if (!self->f) {
		self->f = cmpack_match_init();
		if (!self->f) {
			PyErr_SetString(PyExc_MemoryError, "Memory allocation error");
			return -1;
		}
	}

	if (PhtFile_Check(file)) {
		res = cmpack_match_readref_pht(self->f, ((PhtFile*)file)->f);
		if (res!=0) {
			cmpack_error(res);
			return -1;
		}
	} else
	if (CatFile_Check(file)) {
		res = cmpack_match_readref_cat(self->f, ((CatFile*)file)->f);
		if (res!=0) {
			cmpack_error(res);
			return -1;
		}
	} else {
		PyErr_SetString(PyExc_TypeError, "The function expects a PhtFile object or a CatFile object.");
		return -1;
	}

	cmpack_match_set_maxstars(self->f, readstars);
	cmpack_match_set_vertices(self->f, identstars);
	cmpack_match_set_threshold(self->f, clipping);
	cmpack_match_set_method(self->f, method);
	cmpack_match_set_maxoffset(self->f, maxoffset);
	return 0;
}

static PyObject *
Match_readstars(Match *self, void *data)
{
	return PyLong_FromLong(cmpack_match_get_maxstars(self->f));
}

static PyObject *
Match_identstars(Match *self, void *data)
{
	return PyLong_FromLong(cmpack_match_get_vertices(self->f));
}

static PyObject *
Match_method(Match *self, void *data)
{
	return PyLong_FromLong(cmpack_match_get_method(self->f));
}

static PyObject *
Match_clipping(Match *self, void *data)
{
	return PyFloat_FromDouble(cmpack_match_get_threshold(self->f));
}

static PyObject *
Match_maxoffset(Match *self, void *data)
{
	return PyFloat_FromDouble(cmpack_match_get_maxoffset(self->f));
}

static PyObject *
Match_run(Match *self, PyObject *param)
{
	int res, nstars;
	double offset[2];
	PhtFile *file;

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

	res = cmpack_match(self->f, file->f, &nstars);
	if (res==CMPACK_ERR_MATCH_NOT_FOUND) {
		Py_RETURN_NONE;
	}
	if (res!=0) {
		cmpack_error(res);
		return NULL;
	}
	cmpack_match_get_offset(self->f, &offset[0], &offset[1]);
	return Py_BuildValue("idd", nstars, offset[0], offset[1]);
}

static PyGetSetDef Match_getset[] = {
	{"readstars", (getter)Match_readstars, NULL, 
	"Max. number of stars used for matching"},
	{"identstars", (getter)Match_identstars, NULL, 
	"Number of stars used for making a polygon"},
	{"method", (getter)Match_method, NULL, 
	"Matching method"},
	{"clipping", (getter)Match_clipping, NULL, 
	"Clipping threshold"},
	{"maxoffset", (getter)Match_maxoffset, NULL, 
	"Max. offset for 'sparse fields' method"},
	{NULL}
};

static PyMethodDef Match_methods[] = {
	{"run", (PyCFunction)Match_run, METH_VARARGS,
	"Execute matching"},
    {NULL}  /* Sentinel */
};

PyTypeObject MatchType = {
    PyObject_HEAD_INIT(NULL)
    .tp_name = "cmpack.Match",          /*tp_name*/
    .tp_basicsize = sizeof(Match),           /*tp_basicsize*/
    .tp_dealloc = (destructor)Match_dealloc,           /*tp_dealloc*/
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /*tp_flags*/
    .tp_doc = "Match",			/* tp_doc */
    .tp_methods = Match_methods,           /* tp_methods */
    .tp_getset = Match_getset,            /* tp_getset */
    .tp_init = (initproc)Match_init,      /* tp_init */
    .tp_new = Match_new                 /* tp_new */
};
