// -*- panstarrs-c -*-
%nodefault;

%include "exception.i"
#if !defined(SWIGXML)
%include "typemaps.i"
#endif
%include "carrays.i"

%array_class(int, intArray);
%array_class(float, floatArray);
%array_class(double, doubleArray);

/************************************************************************************************************/

#if defined(SWIGPYTHON)
%pythoncode {
class PsUtilsError(StandardError):
	"""An error generated by psUtils"""
}
#endif

/************************************************************************************************************/

%{
#  include "pslib.h"

static void *exception(int exception_type, const char *fmt, ...)
{
    char message[256];
    va_list ap;

    va_start(ap, fmt);
    vsnprintf(message, 256, fmt, ap);
    va_end(ap);

    SWIG_exception(exception_type, message)

 fail:					// used by SWIG_exception
    return NULL;
} 

static void *ps_exception(void)
{
    psErr *err = psErrorLast();
    char message[256];
    snprintf(message, 256, "(%s) %s", err->name, err->msg);
    psFree(err);

    SWIG_exception(SWIG_RuntimeError, message)

 fail:					// used by SWIG_exception
    return NULL;
} 

#if 0
static PyObject *set_thisown(PyObject *resultobj, int val)
{
    PyObject *n = PyInt_FromLong(val);
    PyObject_SetAttrString(resultobj, (char *)"thisown", n);
    Py_DECREF(n);

    return resultobj;
}
#endif

%}

/************************************************************************************************************/
/*
 * Typemaps
 */
%typemap(python, out) psStatus {
    if (result == 0) {
	Py_INCREF(Py_None); resultobj = Py_None;
    } else {
	ps_exception();
	return NULL;
    }
}

%typemap(python,in) FILE * {
    if ($input == Py_None) {
	$1 = NULL;
    } else if (!PyFile_Check($input)) {
	PyErr_SetString(PyExc_TypeError, "Need a file!");
	goto fail;
    } else {
	$1 = PyFile_AsFile($input);
    }
}

%typemap(python,in) char * {
    if ($input == Py_None) {
	$1 = NULL;
    } else if (!PyString_Check($input)) {
	PyErr_SetString(PyExc_TypeError, "Need a string!");
	goto fail;
    } else {
	$1 = PyString_AsString($input);
    }
}

/************************************************************************************************************/
/*
 * Tell our extension language that this function returns a new object (that can't be NULL)
 */
%define NEWOBJECT(func)
    %newobject func;
    NOTNULL(func);
%enddef

/************************************************************************************************************/
/*
 * Tell our extension language that it should call psFree to delete an object
 */
%define EXPORT_TYPE(type)
    %extend type {
	~type() {
	    if (psMemGetRefCounter(self) == 1) {
		const psMemBlock *mb = ((psMemBlock *)self) - 1;

#if !defined(PS_NO_TRACE)
		p_psTrace("ps.utils.memory", 6, "Freeing %p (%ld: %s:%d)\n", self, (long)mb->id,
			  mb->file, mb->lineno);
#endif
	    }
	    psFree(self);
	}
    }

    %{
        type *type ## _Cast(void *ptr) {
	    return (type *)ptr;
	}
    %}
    type *type ## _Cast(void *ptr);

    NEWOBJECT(type ## Alloc);
%enddef

/************************************************************************************************************/
/*
 * Throw an exception if func returns NULL
 */
%define NOTNULL(func)
    %exception func {
        $action;
	if (result == NULL) {
	    $cleanup;
#if defined(SWIGPERL) 
	    ps_exception();
#else
	    return ps_exception();
#endif
	}
    }
%enddef

/*
 * Throw an exception if func returns a negative value
 */
%define NOTNEGATIVE(func)
    %exception func {
        $action;
	if (result < 0) {
	    $cleanup;
#if defined(SWIGPERL) 
	    ps_exception();
#else
	    return ps_exception();
#endif
	}
    }
%enddef

/*
 * Throw an exception if func returns non-zero
 */
%define ISZERO(func)
    %exception func {
        $action;
	if (result != 0) {
	    $cleanup;
#if defined(SWIGPERL) 
	    ps_exception();
#else
	    return ps_exception();
#endif
	}
    }
%enddef

/************************************************************************************************************/
/*
 * If an argument is passed in, and also returned by the function, we need to declare
 * the function %newobject, and also ensure that the input function has its refCounter
 * incremented (as perl/python etc. will think that it's gone out of scope)
 *
 * This macro defined a typemap to handle the refcounter.
 *
 * Do this as an argout map as we don't get another reference to the image if there's an error
 */
%define TYPE_INOUT(type, argname)
    %typemap(argout) type argname {
        if ($1 != NULL) {
	    const psMemBlock *mb = ((psMemBlock *)$1) - 1;
	    p_psTrace("ps.utils.memory", 4, "Incrementing reference counter for %p (%ld %s:%d)\n",
		      $1, (long)mb->id, mb->file, mb->lineno);
	    psMemIncrRefCounter($1);
	}
    }
%enddef

%define CLEAR_TYPE_INOUT(type, argname)
    %typemap(argout) type argname;
%enddef

/************************************************************************************************************/

%exception;

/************************************************************************************************************/
/*
 * Accept a python list of floats if the routine wants an array
 */
%typemap(in) (const float *floatArr, int nel) {
    /* Check if is a list */
    if (PyList_Check($input)) {
	$2 = PyList_Size($input);
	if ($2 == 0) {
	    $1 = NULL;
	} else {
	    $1 = psAlloc($2*sizeof(float));
	
	    for (int i = 0; i < $2; i++) {
		PyObject *o = PyList_GetItem($input,i);
		if (PyFloat_Check(o))
		    $1[i] = PyFloat_AsDouble(PyList_GetItem($input, i));
		else {
		    PyErr_SetString(PyExc_TypeError, "list must contain floats");
		    psFree($1);
		    return NULL;
		}
	    }
	}
    } else {
	PyErr_SetString(PyExc_TypeError,"not a list");
	return NULL;
    }
}

%typemap(freearg) (const float *floatArr, int nel) {
    psFree($1);
}

/************************************************************************************************************/

%apply double *OUTPUT { double *irow };
%apply double *OUTPUT { double *icol };
%apply double *OUTPUT { double *xPos };
%apply double *OUTPUT { double *yPos };
%apply double *OUTPUT { double *alpha };
%apply double *OUTPUT { double *delta };
%apply double *OUTPUT { double *alt };
%apply double *OUTPUT { double *az };
%apply double *OUTPUT { double *lst };
