/*\file FetchData.cpp:
 * \brief: general I/O interface to fetch data in python
 */

#ifdef HAVE_CONFIG_H
	#include <config.h>
#else
#error "Cannot compile with HAVE_CONFIG_H symbol! run configure first!"
#endif

#define PY_ARRAY_UNIQUE_SYMBOL PythonIOSymbol
#define NO_IMPORT

#include "./pythonio.h"
#include "../../c/include/include.h"
#include "../../c/shared/shared.h"

/*Primitive data types*/
/*FUNCTION FetchData(double* pscalar,PyObject* py_float){{{*/
void FetchData(double* pscalar,PyObject* py_float){

	double scalar;

	/*return internal value: */
	scalar=PyFloat_AsDouble(py_float);

	/*output: */
	*pscalar=scalar;
}
/*}}}*/
/*FUNCTION FetchData(int* pinteger,PyObject* py_long){{{*/
void FetchData(int* pinteger, PyObject* py_long){

	int integer;

	/*return internal value: */
	integer=(int)PyLong_AsLong(py_long);

	/*output: */
	*pinteger=integer;
}
/*}}}*/
/*FUNCTION FetchData(bool* pboolean,PyObject* py_boolean){{{*/
void FetchData(bool* pboolean,PyObject* py_boolean){

	bool boolean;

	/*check this is indeed a subtype of long type: */
	if(!PyBool_Check(py_boolean))_error_("expecting a boolean in input!");

	/*extract boolean: */
	boolean=(bool)PyLong_AsLong(py_boolean);

	/*simple copy: */
	*pboolean=boolean;

}
/*}}}*/
/*FUNCTION FetchData(double** pmatrix,int* pM, int* pN, PyObject* py_matrix){{{*/
void FetchData(double** pmatrix,int* pM,int *pN,PyObject* py_matrix){

	/*output: */
	double* dmatrix=NULL;
	double* matrix=NULL;
	int M,N;
	int ndim;
	npy_intp*  dims=NULL;

	/*retrive dimensions: */
	ndim=PyArray_NDIM((const PyArrayObject*)py_matrix);
	if(ndim!=2)_error_("expecting an MxN matrix in input!");
	dims=PyArray_DIMS((PyArrayObject*)py_matrix);
	M=dims[0]; N=dims[1];

	if (M && N) {
		/*retrieve internal value: */
		dmatrix=(double*)PyArray_DATA((PyArrayObject*)py_matrix);

		/*copy matrix: */
		matrix=xNew<double>(M*N);
		memcpy(matrix,dmatrix,(M*N)*sizeof(double));
	}
	else
		matrix=NULL;

	/*output: */
	if(pM)*pM=M;
	if(pN)*pN=N;
	if(pmatrix)*pmatrix=matrix;
}
/*}}}*/
/*FUNCTION FetchData(int** pmatrix,int* pM, int* pN, PyObject* py_matrix){{{*/
void FetchData(int** pmatrix,int* pM,int *pN,PyObject* py_matrix){

	/*output: */
	double* dmatrix=NULL;
	int* matrix=NULL;
	int M,N;

	/*intermediary:*/
	int i;
	int ndim;
	npy_intp*  dims=NULL;

	/*retrive dimensions: */
	ndim=PyArray_NDIM((const PyArrayObject*)py_matrix);
	if(ndim!=2)_error_("expecting an MxN matrix in input!");
	dims=PyArray_DIMS((PyArrayObject*)py_matrix);
	M=dims[0]; N=dims[1];

	if (M && N) {
		/*retrieve internal value: */
		dmatrix=(double*)PyArray_DATA((PyArrayObject*)py_matrix);

		/*transform into integer matrix: */
		matrix=xNew<int>(M*N);
		for(i=0;i<M*N;i++)matrix[i]=(int)dmatrix[i];
	}
	else
		matrix=NULL;

	/*output: */
	if(pM)*pM=M;
	if(pN)*pN=N;
	if(pmatrix)*pmatrix=matrix;
}
/*}}}*/
/*FUNCTION FetchData(double** pvector,int* pM, PyObject* py_vector){{{*/
void FetchData(double** pvector,int* pM,PyObject* py_vector){

	/*output: */
	double* dvector=NULL;
	double* vector=NULL;
	int M;
	int ndim;
	npy_intp*  dims=NULL;

	/*retrive dimensions: */
	ndim=PyArray_NDIM((const PyArrayObject*)py_vector);
	if(ndim!=1)_error_("expecting an Mx1 vector in input!");
	dims=PyArray_DIMS((PyArrayObject*)py_vector);
	M=dims[0]; 

	if (M) {
		/*retrieve internal value: */
		dvector=(double*)PyArray_DATA((PyArrayObject*)py_vector);

		/*copy vector: */
		vector=xNew<double>(M);
		memcpy(vector,dvector,(M)*sizeof(double));
	}
	else
		vector=NULL;

	/*output: */
	if(pM)*pM=M;
	if(pvector)*pvector=vector;
}
/*}}}*/

/*ISSM objects*/
/*FUNCTION FetchData(BamgGeom** pbamggeom,PyObject* py_dict){{{*/
void FetchData(BamgGeom** pbamggeom,PyObject* py_dict){

	/*Initialize output*/
	BamgGeom* bamggeom=new BamgGeom();

	/*Fetch all fields*/
	FetchData(&bamggeom->Vertices,&bamggeom->VerticesSize[0],&bamggeom->VerticesSize[1],PyDict_GetItemString(py_dict,"Vertices"));
	FetchData(&bamggeom->Edges, &bamggeom->EdgesSize[0], &bamggeom->EdgesSize[1], PyDict_GetItemString(py_dict,"Edges"));
	FetchData(&bamggeom->Corners, &bamggeom->CornersSize[0], &bamggeom->CornersSize[1], PyDict_GetItemString(py_dict,"Corners"));
	FetchData(&bamggeom->RequiredVertices,&bamggeom->RequiredVerticesSize[0],&bamggeom->RequiredVerticesSize[1],PyDict_GetItemString(py_dict,"RequiredVertices"));
	FetchData(&bamggeom->RequiredEdges, &bamggeom->RequiredEdgesSize[0], &bamggeom->RequiredEdgesSize[1], PyDict_GetItemString(py_dict,"RequiredEdges"));
	FetchData(&bamggeom->CrackedEdges,&bamggeom->CrackedEdgesSize[0],&bamggeom->CrackedEdgesSize[1],PyDict_GetItemString(py_dict,"CrackedEdges"));
	FetchData(&bamggeom->SubDomains,&bamggeom->SubDomainsSize[0],&bamggeom->SubDomainsSize[1],PyDict_GetItemString(py_dict,"SubDomains"));

	/*Assign output pointers:*/
	*pbamggeom=bamggeom;
}
/*}}}*/
/*FUNCTION FetchData(BamgMesh** pbamgmesh,PyObject* py_dict){{{*/
void FetchData(BamgMesh** pbamgmesh,PyObject* py_dict){

	/*Initialize output*/
	BamgMesh* bamgmesh=new BamgMesh();

	/*Fetch all fields*/
	FetchData(&bamgmesh->Vertices,&bamgmesh->VerticesSize[0],&bamgmesh->VerticesSize[1],PyDict_GetItemString(py_dict,"Vertices"));
	FetchData(&bamgmesh->Edges, &bamgmesh->EdgesSize[0], &bamgmesh->EdgesSize[1], PyDict_GetItemString(py_dict,"Edges"));
	FetchData(&bamgmesh->Triangles, &bamgmesh->TrianglesSize[0], &bamgmesh->TrianglesSize[1], PyDict_GetItemString(py_dict,"Triangles"));
	FetchData(&bamgmesh->CrackedEdges,&bamgmesh->CrackedEdgesSize[0],&bamgmesh->CrackedEdgesSize[1],PyDict_GetItemString(py_dict,"CrackedEdges"));
	FetchData(&bamgmesh->VerticesOnGeomEdge,&bamgmesh->VerticesOnGeomEdgeSize[0],&bamgmesh->VerticesOnGeomEdgeSize[1],PyDict_GetItemString(py_dict,"VerticesOnGeomEdge"));
	FetchData(&bamgmesh->VerticesOnGeomVertex,&bamgmesh->VerticesOnGeomVertexSize[0],&bamgmesh->VerticesOnGeomVertexSize[1],PyDict_GetItemString(py_dict,"VerticesOnGeomVertex"));
	FetchData(&bamgmesh->EdgesOnGeomEdge, &bamgmesh->EdgesOnGeomEdgeSize[0], &bamgmesh->EdgesOnGeomEdgeSize[1], PyDict_GetItemString(py_dict,"EdgesOnGeomEdge"));
	FetchData(&bamgmesh->IssmSegments,&bamgmesh->IssmSegmentsSize[0],&bamgmesh->IssmSegmentsSize[1],PyDict_GetItemString(py_dict,"IssmSegments"));

	/*Assign output pointers:*/
	*pbamgmesh=bamgmesh;
}
/*}}}*/
/*FUNCTION FetchData(BamgOpts** pbamgopts,PyObject* py_dict){{{*/
void FetchData(BamgOpts** pbamgopts,PyObject* py_dict){

	/*Initialize output*/
	BamgOpts* bamgopts=new BamgOpts();

	/*Fetch all fields*/
	FetchData(&bamgopts->anisomax,PyDict_GetItemString(py_dict,"anisomax"));
	FetchData(&bamgopts->cutoff,PyDict_GetItemString(py_dict,"cutoff"));
	FetchData(&bamgopts->coeff,PyDict_GetItemString(py_dict,"coeff"));
	FetchData(&bamgopts->errg,PyDict_GetItemString(py_dict,"errg"));
	FetchData(&bamgopts->gradation,PyDict_GetItemString(py_dict,"gradation"));
	FetchData(&bamgopts->Hessiantype,PyDict_GetItemString(py_dict,"Hessiantype"));
	FetchData(&bamgopts->MaxCornerAngle,PyDict_GetItemString(py_dict,"MaxCornerAngle"));
	FetchData(&bamgopts->maxnbv,PyDict_GetItemString(py_dict,"maxnbv"));
	FetchData(&bamgopts->maxsubdiv,PyDict_GetItemString(py_dict,"maxsubdiv"));
	FetchData(&bamgopts->Metrictype,PyDict_GetItemString(py_dict,"Metrictype"));
	FetchData(&bamgopts->nbjacobi,PyDict_GetItemString(py_dict,"nbjacobi"));
	FetchData(&bamgopts->nbsmooth,PyDict_GetItemString(py_dict,"nbsmooth"));
	FetchData(&bamgopts->omega,PyDict_GetItemString(py_dict,"omega"));
	FetchData(&bamgopts->power,PyDict_GetItemString(py_dict,"power"));
	FetchData(&bamgopts->verbose,PyDict_GetItemString(py_dict,"verbose"));

	FetchData(&bamgopts->Crack,PyDict_GetItemString(py_dict,"Crack"));
	FetchData(&bamgopts->geometricalmetric,PyDict_GetItemString(py_dict,"geometricalmetric"));
	FetchData(&bamgopts->KeepVertices,PyDict_GetItemString(py_dict,"KeepVertices"));
	FetchData(&bamgopts->splitcorners,PyDict_GetItemString(py_dict,"splitcorners"));

	FetchData(&bamgopts->hmin,PyDict_GetItemString(py_dict,"hmin"));
	FetchData(&bamgopts->hmax,PyDict_GetItemString(py_dict,"hmax"));
	FetchData(&bamgopts->hminVertices,&bamgopts->hminVerticesSize[0],&bamgopts->hminVerticesSize[1],PyDict_GetItemString(py_dict,"hminVertices"));
	FetchData(&bamgopts->hmaxVertices,&bamgopts->hmaxVerticesSize[0],&bamgopts->hmaxVerticesSize[1],PyDict_GetItemString(py_dict,"hmaxVertices"));
	FetchData(&bamgopts->hVertices,&bamgopts->hVerticesSize[0],&bamgopts->hVerticesSize[1],PyDict_GetItemString(py_dict,"hVertices"));
	FetchData(&bamgopts->metric,&bamgopts->metricSize[0],&bamgopts->metricSize[1],PyDict_GetItemString(py_dict,"metric"));
	FetchData(&bamgopts->field,&bamgopts->fieldSize[0],&bamgopts->fieldSize[1],PyDict_GetItemString(py_dict,"field"));
	FetchData(&bamgopts->err,&bamgopts->errSize[0],&bamgopts->errSize[1],PyDict_GetItemString(py_dict,"err"));

	/*Additional checks*/
	bamgopts->Check();

	/*Assign output pointers:*/
	*pbamgopts=bamgopts;
}
/*}}}*/
/*FUNCTION FetchData(Options** poptions,int istart, int nrhs,PyObject* py_tuple){{{*/
void FetchData(Options** poptions,int istart, int nrhs,PyObject* py_tuple){

	char   *name   = NULL;
	Option *option = NULL;

	/*Initialize output*/
	Options* options=new Options();

	/*Fetch all options*/
	for (int i=istart; i<nrhs; i=i+2){
		if (!PyString_Check(PyTuple_GetItem(py_tuple,(Py_ssize_t)i))) _error_("Argument " << i+1 << " must be name of option");

		FetchData(&name,PyTuple_GetItem(py_tuple,(Py_ssize_t)i));
		if(i+1 == nrhs) _error_("Argument " << i+2 << " must exist and be value of option \"" << name << "\".");

		_pprintLine_("FetchData for Options not implemented yet, ignoring option \"" << name << "\"!");

//		option=(Option*)OptionParse(name,&PyTuple_GetItem(py_tuple,(Py_ssize_t)(i+1)));
//		options->AddOption(option);
//		option=NULL;
	}

	/*Assign output pointers:*/
	*poptions=options;
}
/*}}}*/
/*FUNCTION FetchData(DataSet** pcontours,PyObject* py_list){{{*/
void FetchData(DataSet** pcontours,PyObject* py_list){

	int              numcontours,test1,test2;
	char            *contourname = NULL;
	DataSet         *contours    = NULL;
	Contour<double> *contouri    = NULL;
	PyObject        *py_dicti    = NULL;
	PyObject        *py_item     = NULL;

	if (PyString_Check(py_list)){
		FetchData(&contourname,py_list);
		contours=DomainOutlineRead<double>(contourname);
	}
	else if(PyList_Check(py_list)){

		contours=new DataSet(0);
		numcontours=(int)PyList_Size(py_list);

		for(int i=0;i<numcontours;i++){

			contouri=xNew<Contour<double> >(1);
			py_dicti=PyList_GetItem(py_list,(Py_ssize_t)i);

			py_item = PyDict_GetItemString(py_dicti,"nods");
			if(!py_item) _error_("input structure does not have a 'nods' field");
			FetchData(&contouri->nods,py_item);

			py_item = PyDict_GetItemString(py_dicti,"x");
			if(!py_item) _error_("input structure does not have a 'x' field");
			FetchData(&contouri->x,&test1,&test2,py_item);
			if(test1!=contouri->nods || test2!=1) _error_("field x should be of size ["<<contouri->nods<<" 1]");

			py_item = PyDict_GetItemString(py_dicti,"y");
			if(!py_item) _error_("input structure does not have a 'y' field");
			FetchData(&contouri->y,&test1,&test2,py_item);
			if(test1!=contouri->nods || test2!=1) _error_("field y should be of size ["<<contouri->nods<<" 1]");

			contours->AddObject(contouri);
		}
	}
	else{
		_error_("Contour is neither a string nor a structure and cannot be loaded");
	}

	/*clean-up and assign output pointer*/
	xDelete<char>(contourname);
	*pcontours=contours;
}
/*}}}*/

/*Python version dependent: */
#if _PYTHON_MAJOR_ >= 3 
/*FUNCTION FetchData(char** pstring,PyObject* py_unicode){{{*/
void FetchData(char** pstring,PyObject* py_unicode){

	PyObject* py_bytes;
	char* string=NULL;

	/*convert to bytes format: */
	PyUnicode_FSConverter(py_unicode,&py_bytes);

	/*convert from bytes to string: */
	string=PyBytes_AS_STRING(py_bytes);

	*pstring=string;
}
/*}}}*/
#else
/*FUNCTION FetchData(char** pstring,PyObject* py_string){{{*/
void FetchData(char** pstring,PyObject* py_string){

	char* string=NULL;

	/*extract internal string: */
	string=PyString_AsString(py_string);

	/*copy string (note strlen does not include trailing NULL): */
	*pstring=xNew<char>(strlen(string)+1);
	memcpy(*pstring,string,(strlen(string)+1)*sizeof(char));
}
/*}}}*/
#endif
