/*!\file:  ParameterInputs.cpp
 * \brief  routines for loading from matlab the parameter inputs to our non-linear iterations.
 */ 


#include "./ParameterInputs.h"
#include "../shared/shared.h"
#include "../include/macros.h"

/*--------------------------------------------------
	NewParameterInputs
  --------------------------------------------------*/

ParameterInputs* NewParameterInputs(void) {
	
	ParameterInputs*	inputs=NULL;

	inputs=(ParameterInputs*)xmalloc(sizeof(ParameterInputs));

	inputs->nfields=0;
	inputs->fields=NULL;
	inputs->field_names=NULL;
	inputs->fields_length=NULL;
	
	return inputs;
}


/*--------------------------------------------------
	ParameterInputsEcho	
  --------------------------------------------------*/
int ParameterInputsEcho(ParameterInputs* inputs,int longecho){

	int noerr=1;
	int i,j;

	/*short echo: */
	if(!longecho){
		_printf_("field names, field pointers: \n");
		for (i=0;i<inputs->nfields;i++){
			_printf_("%s: %p\n",inputs->field_names[i],inputs->fields[i]);
		}
	}
	/*long echo: */
	else{
		for (i=0;i<inputs->nfields;i++){
			double* values=inputs->fields[i];
			_printf_("field names: %s\n",inputs->field_names[i]);
			_printf_("field values: \n");
			for(j=0;j<inputs->fields_length[i];j++){
				printf("  %g\n",values[j]);
			}
		}
	}
	return noerr;
}

/*--------------------------------------------------
	ParameterInputsRecover	
  --------------------------------------------------*/
double* ParameterInputsRecover(ParameterInputs* inputs,char* field_name){

	double* param=NULL;
	int i;
	
	/*Find field_name input field, and return the value: */
	for(i=0;i<inputs->nfields;i++){
		if (strcmp(inputs->field_names[i],field_name)==0){
			param=inputs->fields[i];
		}
	}
	return param;
}

/*--------------------------------------------------
	DeleteParameterInputs	
  --------------------------------------------------*/
void DeleteParameterInputs(ParameterInputs** pinputs){

	ParameterInputs* inputs=NULL;
	int i;
	char*  field_name=NULL;
	double* field=NULL;

	#ifdef _PARALLEL_ //inputs operation is only done on the cluster, on the serial side, we leave the matlab memory manager handle inputs.
	inputs=*pinputs;

	if(inputs){

		for(i=0;i<inputs->nfields;i++){
			field_name=inputs->field_names[i];
			xfree((void**)&field_name);

			/*In parallel, erase: */
			field=inputs->fields[i];
			xfree((void**)&field);
		}

		xfree((void**)&inputs->field_names);
		xfree((void**)&inputs->fields);
		xfree((void**)&inputs->fields_length);
	}
	xfree((void**)pinputs);
	#else
	#endif
	return;
}

/*--------------------------------------------------
	ParameterInputsAddFromVec	
  --------------------------------------------------*/
int  ParameterInputsAddFromVec(ParameterInputs* inputs,Vec vector,char* pfield_name){
	
	int     noerr=1;
	double* double_vector=NULL;
	double* double_vector2=NULL;
	int     vector_size;
	char*   field_name;
	char**  field_names;
	int     field_name_size;
	double** fields=NULL;
	int*    fields_length;
	int     i;

	/*Take vector, serialize it, and plug it into inputs with field name "field_name": */
	if (vector){
		VecGetSize(vector,&vector_size);
		VecToMPISerial(&double_vector,vector);
		
		if(inputs->nfields==0){
			/*Ok, we need to allocate fields, field_names and fields_length: */
			inputs->nfields=1;
			
			inputs->field_names=(char**)xmalloc(sizeof(char*));
			field_name_size=strlen(pfield_name)+1;
			field_name=(char*)xmalloc(field_name_size*sizeof(char));
			strcpy(field_name,pfield_name);
			inputs->field_names[0]=field_name;

			inputs->fields=(double**)xmalloc(sizeof(double*));
			inputs->fields[0]=double_vector;

			inputs->fields_length=(int*)xmalloc(sizeof(int));
			inputs->fields_length[0]=vector_size;
		}
		else{
			/*First, figure out if there is already an existing field with name pfield_name: */
			for(i=0;i<inputs->nfields;i++){
				if (strcmp(inputs->field_names[i],pfield_name)==0){
					/*Erase old field and plug new field: */
					double_vector2=inputs->fields[i];
					xfree((void**)&double_vector2); //no leak!
					inputs->fields[i]=double_vector;
					inputs->fields_length[i]=vector_size;
					return noerr;
				}
			}

			/*Ok, if we are here, the field did not already exist, we need to add it. First reallocate 
			 * the inputs: */
			field_names=inputs->field_names; //save field_names pointer.
			inputs->field_names=(char**)xmalloc((inputs->nfields+1)*sizeof(char*));
			for(i=0;i<inputs->nfields;i++){
				inputs->field_names[i]=field_names[i];
			}
			xfree((void**)&field_names);

			field_name_size=strlen(pfield_name)+1;
			field_name=(char*)xmalloc(field_name_size*sizeof(char));
			strcpy(field_name,pfield_name);
			inputs->field_names[inputs->nfields]=field_name;



			fields=inputs->fields; //save fields pointer
			inputs->fields=(double**)xmalloc((inputs->nfields+1)*sizeof(double*));
			for(i=0;i<inputs->nfields;i++){
				inputs->fields[i]=fields[i];
			}
			xfree((void**)&fields);
			inputs->fields[inputs->nfields]=double_vector;

			fields_length=inputs->fields_length;
			inputs->fields_length=(int*)xmalloc((inputs->nfields+1)*sizeof(int));
			for(i=0;i<inputs->nfields;i++){
				inputs->fields_length[i]=fields_length[i];
			}
			inputs->fields_length[inputs->nfields]=vector_size;
			xfree((void**)&fields_length);
			
			inputs->nfields++;
		}
	}
	else{
		/*Do nothing, there is no input to add!: */
		;
	}
	return noerr;
}

/*--------------------------------------------------
	ParameterInputsAddFromDouble	
  --------------------------------------------------*/
int  ParameterInputsAddFromDouble(ParameterInputs* inputs,double scalar, char* pfield_name){
	
	int     noerr=1;
	double* double_vector2=NULL;
	double* double_vector=NULL;
	int     vector_size;
	char*   field_name;
	char**  field_names;
	int     field_name_size;
	double** fields=NULL;
	int*    fields_length;
	int     i;

	/*Create double_vector pointing to scalar: */
	vector_size=1;
	double_vector=(double*)xmalloc(sizeof(double));
	double_vector[0]=scalar;


	if(inputs->nfields==0){
		/*Ok, we need to allocate fields, field_names and fields_length: */
		inputs->nfields=1;
		
		inputs->field_names=(char**)xmalloc(sizeof(char*));
		field_name_size=strlen(pfield_name)+1;
		field_name=(char*)xmalloc(field_name_size*sizeof(char));
		strcpy(field_name,pfield_name);
		inputs->field_names[0]=field_name;

		inputs->fields=(double**)xmalloc(sizeof(double*));
		inputs->fields[0]=double_vector;

		inputs->fields_length=(int*)xmalloc(sizeof(int));
		inputs->fields_length[0]=vector_size;
	}
	else{
		/*First, figure out if there is already an existing field with name pfield_name: */
		for(i=0;i<inputs->nfields;i++){
			if (strcmp(inputs->field_names[i],pfield_name)==0){
				/*Erase old field and plug new field: */
				double_vector2=inputs->fields[i];
				xfree((void**)&double_vector2); //no leak!
				inputs->fields[i]=double_vector;
				inputs->fields_length[i]=vector_size;
				return noerr;
			}
		}

		/*Ok, if we are here, the field did not already exist, we need to add it. First reallocate 
		 * the inputs: */
		field_names=inputs->field_names; //save field_names pointer.
		inputs->field_names=(char**)xmalloc((inputs->nfields+1)*sizeof(char*));
		for(i=0;i<inputs->nfields;i++){
			inputs->field_names[i]=field_names[i];
		}
		xfree((void**)&field_names);

		field_name_size=strlen(pfield_name)+1;
		field_name=(char*)xmalloc(field_name_size*sizeof(char));
		strcpy(field_name,pfield_name);
		inputs->field_names[inputs->nfields]=field_name;



		fields=inputs->fields; //save fields pointer
		inputs->fields=(double**)xmalloc((inputs->nfields+1)*sizeof(double*));
		for(i=0;i<inputs->nfields;i++){
			inputs->fields[i]=fields[i];
		}
		xfree((void**)&fields);
		inputs->fields[inputs->nfields]=double_vector;

		fields_length=inputs->fields_length;
		inputs->fields_length=(int*)xmalloc((inputs->nfields+1)*sizeof(int));
		for(i=0;i<inputs->nfields;i++){
			inputs->fields_length[i]=fields_length[i];
		}
		inputs->fields_length[inputs->nfields]=vector_size;
		xfree((void**)&fields_length);
		
		inputs->nfields++;
	}
	return noerr;
}

/*--------------------------------------------------
	ParameterInputsAddFromMat	
  --------------------------------------------------*/
int  ParameterInputsAddFromMat(ParameterInputs* inputs,double* vector,int vector_size,char* pfield_name){
	
	int     noerr=1;
	char*   field_name;
	char**  field_names;
	int     field_name_size;
	double** fields=NULL;
	int*    fields_length;
	double* double_vector=NULL;
	double* double_vector2=NULL;
	int     i;

	/*Make a copy of input vector, so as to not create any leaks: */
	double_vector=(double*)xmalloc(vector_size*sizeof(double));
	memcpy(double_vector,vector,vector_size*sizeof(double));

	/*Take vector, serialize it, and plug it into inputs with field name "field_name": */
	if (vector){
		
		if(inputs->nfields==0){
			/*Ok, we need to allocate fields, field_names and fields_length: */
			inputs->nfields=1;
			
			inputs->field_names=(char**)xmalloc(sizeof(char*));
			field_name_size=strlen(pfield_name)+1;
			field_name=(char*)xmalloc(field_name_size*sizeof(char));
			strcpy(field_name,pfield_name);
			inputs->field_names[0]=field_name;

			inputs->fields=(double**)xmalloc(sizeof(double*));
			inputs->fields[0]=double_vector;

			inputs->fields_length=(int*)xmalloc(sizeof(int));
			inputs->fields_length[0]=vector_size;
		}
		else{

			/*First, figure out if there is already an existing field with name pfield_name: */
			for(i=0;i<inputs->nfields;i++){
				if (strcmp(inputs->field_names[i],pfield_name)==0){
					/*Just xfree old field and plug new field: */
					double_vector2=inputs->fields[i];
					xfree((void**)&double_vector2);
					inputs->fields[i]=double_vector;
					inputs->fields_length[i]=vector_size;
					return noerr;
				}
			}

			/*Ok, if we are here, the field did not already exist, we need to add it. First reallocate 
			 * the inputs: */
			field_names=inputs->field_names; //save field_names pointer.
			inputs->field_names=(char**)xmalloc((inputs->nfields+1)*sizeof(char*));
			for(i=0;i<inputs->nfields;i++){
				inputs->field_names[i]=field_names[i];
			}
			xfree((void**)&field_names);

			field_name_size=strlen(pfield_name)+1;
			field_name=(char*)xmalloc(field_name_size*sizeof(char));
			strcpy(field_name,pfield_name);
			inputs->field_names[inputs->nfields]=field_name;


			fields=inputs->fields; //save fields pointer
			inputs->fields=(double**)xmalloc((inputs->nfields+1)*sizeof(double*));
			for(i=0;i<inputs->nfields;i++){
				inputs->fields[i]=fields[i];
			}
			xfree((void**)&fields);
			inputs->fields[inputs->nfields]=double_vector;

			fields_length=inputs->fields_length;
			inputs->fields_length=(int*)xmalloc((inputs->nfields+1)*sizeof(int));
			for(i=0;i<inputs->nfields;i++){
				inputs->fields_length[i]=fields_length[i];
			}
			inputs->fields_length[inputs->nfields]=vector_size;
			xfree((void**)&fields_length);
			
			inputs->nfields++;
		}
	}
	else{
		/*Do nothing, there is no input to add!: */
		;
	}
	return noerr;
}
