/*  \file * SerialFetchData.c
 *  \brief fetch data from the matlab workspace, and pass it to the "x" code layer
 */

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

#include "../shared/shared.h"

#ifdef _SERIAL_

#undef __FUNCT__ 
#define __FUNCT__ "SerialFetchData"

void SerialFetchData(void** pdata,int* pM,int* pN,const mxArray* mxdata,char* data_type,char* sub_data_type){

	
	int i,j;


	/*output*/
	DataSet* outdataset=NULL;
	char*    outdataset_buffer=NULL;
	int      outdataset_size;

	double*  outmatrix;
	Mat      outpetscmatrix;
	double*  outmatrix_workspace=NULL;;
	int      outmatrix_rows,outmatrix_cols;
	int      petsc;

	double*  outvector;
	Vec      outpetscvector;
	double*  outvector_workspace=NULL;
	int      outvector_rows;

	char*    outstring=NULL;
	double   outscalar;
	
	/*First branch on the type of data. Allowed types are DataSet, Matrix, Vector, Integer, Scalar and String. Matrix and Vector will 
	 * behave the same way on the client side, differently on the server side. : */
	if (strcmp(data_type,"DataSet")==0){

		/*First, check that our reference is a double, otherwise, error out: */
		if (mxIsDouble(mxdata)){
			/*We need to copy the data pointed by mxdata, so that our dataset is not actually a pointer!:*/
			if (!mxIsEmpty(mxdata)){
				outdataset_buffer=(char*)mxGetPr(mxdata);
				outdataset_size=mxGetM(mxdata)*mxGetN(mxdata);
				if(outdataset_size)outdataset=DataSetDemarshall(outdataset_buffer);
			}
		}
		else{
			if (mxIsEmpty(mxdata)){
				/*Nothing to pick up. Just initialize pointer to NULL, and warn the server we are not uploading anything: */
				outdataset_size=0;
				outdataset=NULL;
			}
			else{
				/*This is an error: we don't have the correct input!: */
				throw ErrorException(__FUNCT__,"  wrong input parameter!");
			}
		}
	}
	else if (strcmp(data_type,"Matrix")==0){
		if(mxIsEmpty(mxdata)){
			/*Nothing to pick up. Just initialize matrix pointer to NULL: */
			outmatrix_rows=0;
			outmatrix_cols=0;
			outmatrix=NULL;
			petsc=0;
		}
		else if (mxIsDouble(mxdata) ){

			/*Look at the sub_type: do we want a double* matrix (Mat) or a petsc matrix (PM)?*/
			if (sub_data_type && strcmp(sub_data_type,"Mat")==0){
				/*Convert matlab matrix to double* matrix: */
				MatlabMatrixToDoubleMatrix(&outmatrix,&outmatrix_rows,&outmatrix_cols,mxdata);
				petsc=0;
			}
			else{
				/*Convert matlab matrix to petsc matrix: */
				MatlabMatrixToPetscMatrix(&outpetscmatrix,&outmatrix_rows,&outmatrix_cols,mxdata);
				petsc=1;
			}
		}
		else{
			/*This is an error: we don't have the correct input!: */
			throw ErrorException(__FUNCT__,"wrong input parameter");
		}
	}
	else if (strcmp(data_type,"Vector")==0){

		if(mxIsEmpty(mxdata)){
			/*Nothing to pick up. Just initialize matrix pointer to NULL: */
			outvector_rows=0;
			outvector=NULL;
			petsc=0;
		}
		else if (mxIsDouble(mxdata) ){

			/*Look at the sub_type: do we want a double* matrix (Vec) or a petsc vector (PV)?*/
			if (sub_data_type && strcmp(sub_data_type,"Vec")==0){
				/*Convert matlab vector to double*  vector: */
				MatlabVectorToDoubleVector(&outvector,&outvector_rows,mxdata);
				petsc=0;
			}
			else{
				/*Convert matlab vector to petsc vector: */
				MatlabVectorToPetscVector(&outpetscvector,&outvector_rows,mxdata);
				petsc=1;
			}
		}
		else{
			/*This is an error: we don't have the correct input!: */
			throw ErrorException(__FUNCT__,"wrong input parameter");
		}
	}
	else if (strcmp(data_type,"String")==0){
		/*Ok, the string should be coming directly from the matlab workspace: */
		if (!mxIsChar(mxdata)){
			throw ErrorException(__FUNCT__,"input data_type is not a string!");
		}
		else{
			/*Recover the string:*/
			int stringlen;
			
			stringlen = mxGetM(mxdata)*mxGetN(mxdata)+1;
			outstring = (char*)xmalloc(sizeof(mxChar)*stringlen);
			mxGetString(mxdata,outstring,stringlen);
		}
	}
	else if ((strcmp(data_type,"Scalar")==0) || (strcmp(data_type,"Integer")==0)){
		/*The scalar should be coming directly from the matlab workspace. But we are fetching a scalar, not an array of scalars, 
		 *we  therefore don't want to dynamically allocate the scalar. Therefore, we are going to do some pointer gymnastics here. 
		 *We assume that  pdata, which is cast as a (void**) pointer, is actually a (double*), we can do that, because void pointers
		 *can be cast to anything!: */
		if (!mxIsDouble(mxdata)){
			throw ErrorException(__FUNCT__,"input data_type is not a scalar!");
		}
		else{
			/*Recover the double: */
			outscalar=mxGetScalar(mxdata);
		}
	}
	else{
		throw ErrorException(__FUNCT__,"incorrect data type data_type only \"Matrix\", \"DataSet\", \"Vector\", \"Scalar\",\"Integer\"  and \"String\" are allowed");
	}


	/*Assign output pointers: */
	
	if(strcmp(data_type,"DataSet")==0){
		*pdata=(void*)outdataset;
		if (pM)*pM=outdataset_size;
		if (pN)*pN=1;
	}
	if(strcmp(data_type,"Matrix")==0){
		if (petsc){
			*pdata=(void*)outpetscmatrix;
		}
		else{
			*pdata=(void*)outmatrix;
		}
		if (pM)*pM=outmatrix_rows;
		if (pN)*pN=outmatrix_cols;
	}
	if(strcmp(data_type,"Vector")==0){
		if(petsc){
			*pdata=(void*)outpetscvector;
		}
		else{
			*pdata=(void*)outvector;
		}
		if (pM)*pM=outvector_rows;
		if (pN)*pN=1;
	}
	if(strcmp(data_type,"String")==0){
		*pdata=(void*)outstring;
		if (pM)*pM=strlen(outstring)+1;
		if (pN)*pN=1;
	}
	if(strcmp(data_type,"Scalar")==0){
		/*some pointer gymnastics: */
		double* pdouble=(double*)pdata; /*Yes, this is not a mistake, as said before, we do not want to dynamically allocate a scalar!*/
		*pdouble=outscalar;
		if (pM)*pM=1;
		if (pN)*pN=1;
	}
	if(strcmp(data_type,"Integer")==0){
		/*same as Scalar, except we cast the result to an integer: */
		int* pinteger=(int*)pdata; /*Yes, this is not a mistake, as said before, we do not want to dynamically allocate a scalar, or an integer!*/
		*pinteger=(int)outscalar; /*cast the double to an integer*/
		if (pM)*pM=1;
		if (pN)*pN=1;
	}
}
#endif //#ifndef _PARALLEL_
