/*!\file Param.c
 * \brief: implementation of the Param object
 */


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

#include "stdio.h"
#include <string.h>
#include "../EnumDefinitions/EnumDefinitions.h"
#include "../include/macros.h"
#include "../shared/shared.h"
#include "./objects.h"

		
Param::Param(){
	return;
}

#undef __FUNCT__ 
#define __FUNCT__  "Param constructor"
Param::Param(int param_id, char* param_name, int param_type){

	id=param_id;
	strcpy(name,param_name);
	type=param_type;
	if ((param_type!=STRING) & (param_type!=INTEGER) & (param_type!=DOUBLE) & 
		(param_type!=DOUBLEVEC) & (param_type!=DOUBLEMAT) & (param_type!=PETSCVEC) & (param_type!=PETSCMAT) 
		){
		throw ErrorException(__FUNCT__,exprintf("%s%i"," unknow parameter type ",param_type));
	}
}

Param::~Param(){
	return;
}

#undef __FUNCT__ 
#define __FUNCT__  "Param echo"
void Param::Echo(void){

	int i,j;
	
	printf("Param:\n");
	printf("   id: %i\n",id);
	printf("   name: %s\n",name);
	
	switch(type){
		case STRING:
			printf("   string value: %s\n",string);
			break;
	
		case INTEGER:
			printf("   integer value: %i\n",integer);
			break;
	
		case DOUBLE:
			printf("   double value: %g\n",ddouble);
			break;
		
		case DOUBLEVEC:
			printf("   double vector. size: %i\n",M);
			for(i=0;i<M;i++)printf("%g\n",doublevec[i]);
			break;
	
		case DOUBLEMAT:
			printf("   double matrix. size: %i,%i\n",M,N);
			for(i=0;i<M;i++){
				for(j=0;j<N;j++){
					printf("%g ",*(doublemat+N*i+j));
				}
				printf("\n");
			}
			break;

		case PETSCVEC:
			printf("   Petsc vector: \n");
			VecView(vec,PETSC_VIEWER_STDOUT_WORLD);
			break;

		case  PETSCMAT:
			printf("   Petsc matrix: \n");
			MatView(mat,PETSC_VIEWER_STDOUT_WORLD);
			break;

		default:
			throw ErrorException(__FUNCT__,exprintf("%s%i","unknow parameter type ",type));
	}
}
	
#undef __FUNCT__
#define __FUNCT__ "Marshall"

void  Param::Marshall(char** pmarshalled_dataset){

	char* marshalled_dataset=NULL;
	int   enum_type=0;
	double* serial_vec=NULL; 
	double* serial_mat=NULL;

	/*recover marshalled_dataset: */
	marshalled_dataset=*pmarshalled_dataset;

	/*get enum type of Param: */
	enum_type=ParamEnum();
	
	/*marshall enum: */
	memcpy(marshalled_dataset,&enum_type,sizeof(enum_type));marshalled_dataset+=sizeof(enum_type);
	
	/*marshall Param data: */
	memcpy(marshalled_dataset,&id,sizeof(id));marshalled_dataset+=sizeof(id);
	memcpy(marshalled_dataset,&name,sizeof(name));marshalled_dataset+=sizeof(name);
	memcpy(marshalled_dataset,&type,sizeof(type));marshalled_dataset+=sizeof(type);

	switch(type){
		case STRING:
			memcpy(marshalled_dataset,&string,sizeof(string));marshalled_dataset+=sizeof(string);
			break;
		case INTEGER:
			memcpy(marshalled_dataset,&integer,sizeof(integer));marshalled_dataset+=sizeof(integer);
			break;
		case DOUBLE:
			memcpy(marshalled_dataset,&ddouble,sizeof(ddouble));marshalled_dataset+=sizeof(ddouble);
			break;
		case DOUBLEVEC:
			memcpy(marshalled_dataset,&M,sizeof(M));marshalled_dataset+=sizeof(M);
			memcpy(marshalled_dataset,doublevec,M*sizeof(double));marshalled_dataset+=(M*sizeof(double));
			break;

		case DOUBLEMAT:
			memcpy(marshalled_dataset,&M,sizeof(M));marshalled_dataset+=sizeof(M);
			memcpy(marshalled_dataset,&N,sizeof(N));marshalled_dataset+=sizeof(N);
			memcpy(marshalled_dataset,doublevec,M*N*sizeof(double));marshalled_dataset+=(M*N*sizeof(double));
			break;

		case PETSCVEC:
			VecGetSize(vec,&M);
			VecToMPISerial(&serial_vec,vec);
			memcpy(marshalled_dataset,&M,sizeof(M));marshalled_dataset+=sizeof(M);
			memcpy(marshalled_dataset,serial_vec,M*sizeof(double));marshalled_dataset+=(M*sizeof(double));
			xfree((void**)&serial_vec);
			break;

		case PETSCMAT:
			MatGetSize(mat,&M,&N);
			MatToSerial(&serial_mat,mat);
			memcpy(marshalled_dataset,&M,sizeof(M));marshalled_dataset+=sizeof(M);
			memcpy(marshalled_dataset,&N,sizeof(N));marshalled_dataset+=sizeof(N);
			memcpy(marshalled_dataset,serial_mat,M*N*sizeof(double));marshalled_dataset+=(M*N*sizeof(double));
			xfree((void**)&serial_mat);
			break;
		
		default:
			_printf_("cheinnere\n");
			throw ErrorException(__FUNCT__,exprintf("%s%i","unknown parameter type",type));
			break;
	}

	*pmarshalled_dataset=marshalled_dataset;
	return;
}
		
int   Param::MarshallSize(){

	int size;

	size=sizeof(id)+
		sizeof(name)+
		sizeof(type)+
		sizeof(int); //sizeof(int) for enum type

	switch(type){
		case STRING:
			size+=sizeof(string);
			break;
		case INTEGER:
			size+= sizeof(integer);
			break;
		case DOUBLE:
			size+= sizeof(ddouble);
			break;
		case DOUBLEVEC:
			size+= sizeof(M)+M*sizeof(double);
			break;

		case DOUBLEMAT:
			size+= sizeof(M)+sizeof(N)+M*N*sizeof(double);
			break;

		case PETSCVEC:
			size+= sizeof(M)+M*sizeof(double);
			break;

		case PETSCMAT:
			size+= sizeof(M)+sizeof(N)+M*N*sizeof(double);
			break;

		default:
			throw ErrorException(__FUNCT__,exprintf("%s%i","unknown parameter type",type));
	}

	return size;
}

char* Param::GetName(void){
	return "param";
}
		

void  Param::Demarshall(char** pmarshalled_dataset){

	char* marshalled_dataset=NULL;
	double* serial_vec=NULL;
	double* serial_mat=NULL;
	int* idxm=NULL;
	int* idxn=NULL; 
	int i;
	double sparsity=.001;

	/*recover marshalled_dataset: */
	marshalled_dataset=*pmarshalled_dataset;

	/*this time, no need to get enum type, the pointer directly points to the beginning of the 
	 *object data (thanks to DataSet::Demarshall):*/

	memcpy(&id,marshalled_dataset,sizeof(id));marshalled_dataset+=sizeof(id);
	memcpy(&name,marshalled_dataset,sizeof(name));marshalled_dataset+=sizeof(name);
	memcpy(&type,marshalled_dataset,sizeof(type));marshalled_dataset+=sizeof(type);


	switch(type){

		case STRING:
			memcpy(&string,marshalled_dataset,sizeof(string));marshalled_dataset+=sizeof(string);
			break;

		case INTEGER:
			memcpy(&integer,marshalled_dataset,sizeof(integer));marshalled_dataset+=sizeof(integer);
			break;

		case DOUBLE:
			memcpy(&ddouble,marshalled_dataset,sizeof(ddouble));marshalled_dataset+=sizeof(ddouble);
			break;

		case DOUBLEVEC:
	
			memcpy(&M,marshalled_dataset,sizeof(M));marshalled_dataset+=sizeof(M);
			doublevec=(double*)xmalloc(M*sizeof(double));
			memcpy(doublevec,marshalled_dataset,M*sizeof(double));marshalled_dataset+=(M*sizeof(double));
			break;

		case DOUBLEMAT:
			memcpy(&M,marshalled_dataset,sizeof(M));marshalled_dataset+=sizeof(M);
			memcpy(&N,marshalled_dataset,sizeof(N));marshalled_dataset+=sizeof(N);
			doublemat=(double*)xmalloc(M*N*sizeof(double));
			memcpy(doublemat,marshalled_dataset,M*N*sizeof(double));marshalled_dataset+=(M*N*sizeof(double));
			break;

		case PETSCVEC:
			memcpy(&M,marshalled_dataset,sizeof(M));marshalled_dataset+=sizeof(M);
			serial_vec=(double*)xmalloc(M*sizeof(double));
			memcpy(serial_vec,marshalled_dataset,M*sizeof(double));marshalled_dataset+=(M*sizeof(double));

			vec=NewVec(M);
			idxm=(int*)xmalloc(M*sizeof(int));
			for(i=0;i<M;i++)idxm[i]=i;
			VecSetValues(vec,M,idxm,serial_vec,INSERT_VALUES);

			VecAssemblyBegin(vec);
			VecAssemblyEnd(vec);
			
			xfree((void**)&serial_vec);
			xfree((void**)&idxm);
			break;

		case PETSCMAT:
			memcpy(&M,marshalled_dataset,sizeof(M));marshalled_dataset+=sizeof(M);
			memcpy(&N,marshalled_dataset,sizeof(N));marshalled_dataset+=sizeof(N);
			serial_mat=(double*)xmalloc(M*N*sizeof(double));
			memcpy(serial_mat,marshalled_dataset,M*N*sizeof(double));marshalled_dataset+=(M*N*sizeof(double));
			mat=NewMat(M,N,&sparsity,NULL,NULL);
			idxm=(int*)xmalloc(M*sizeof(int));
			idxn=(int*)xmalloc(N*sizeof(int));
			for(i=0;i<M;i++)idxm[i]=i;
			for(i=0;i<N;i++)idxn[i]=i;
			MatSetValues(mat,M,idxm,N,idxn,serial_mat,INSERT_VALUES); 
			MatAssemblyBegin(mat,MAT_FINAL_ASSEMBLY); 
			MatAssemblyEnd(mat,MAT_FINAL_ASSEMBLY);

			xfree((void**)&serial_mat);
			xfree((void**)&idxm);
			xfree((void**)&idxn);
			break;

		default:
			throw ErrorException(__FUNCT__,exprintf("%s%i","unknown parameter type",type));
	}

	/*return: */
	*pmarshalled_dataset=marshalled_dataset;
	return;
}


int Param::Enum(void){

	return ParamEnum();
}

char* Param::GetParameterName(void){ 
	return name; 
}

void  Param::GetParameterValue(void* pvalue){ 

	if (type==STRING){
		char** pstring=(char**)pvalue; //a little dangerous, but hey!
		char*  outstring=NULL;
		outstring=(char*)xmalloc((strlen(string)+1)*sizeof(char));
		strcpy(outstring,string);
		*pstring=outstring;
	}
	else if(type==INTEGER){
		int* pinteger=(int*)pvalue;
		*pinteger=integer;
	}
	else if(type==DOUBLE){
		double* pdouble=(double*)pvalue;
		*pdouble=ddouble;
	}
	else if(type==DOUBLEVEC){
		double** pdoublevec=(double**)pvalue;
		double* outdoublevec=NULL;
		outdoublevec=(double*)xmalloc(M*sizeof(double));
		memcpy(outdoublevec,doublevec,M*sizeof(double));
		*pdoublevec=outdoublevec;
	}
	else if(type==DOUBLEMAT){
		double** pdoublemat=(double**)pvalue;
		double* outdoublemat=NULL;
		outdoublemat=(double*)xmalloc(M*N*sizeof(double));
		memcpy(outdoublemat,doublemat,M*N*sizeof(double));
		*pdoublemat=outdoublemat;
	}
	else if(type==PETSCVEC){
		Vec* pvec=(Vec*)pvalue;
		Vec  outvec=NULL;
		VecDuplicate(vec,&outvec);
		VecCopy(vec,outvec);
		*pvec=outvec;
	}
	else if(type==PETSCMAT){
		Mat* pmat=(Mat*)pvalue;
		Mat  outmat=NULL;
		MatDuplicate(mat,MAT_COPY_VALUES,&outmat);
		*pmat=outmat;
	}
	else{
		_printf_("%s%s%i\n",__FUNCT__," error message: unknow type ",type);
		abort();
	}
	return;
}

int    Param::GetId(void){ return id; }

int    Param::MyRank(void){ 
	extern int my_rank;
	return my_rank; 
}

void  Param::DistributeNumDofs(int* numdofspernode,int analysis_type){return;}
		

int   Param::GetType(){
	return type;
}

Object* Param::copy() {
	return new Param(*this); 
}

#undef __FUNCT__
#define __FUNCT__ "SetDouble"
void  Param::SetDouble(double value){
	if (type!=DOUBLE) throw ErrorException(__FUNCT__,exprintf("%s%i"," trying to set double for type",type));
	ddouble=value;
}

#undef __FUNCT__
#define __FUNCT__ "SetInteger"
void  Param::SetInteger(int value){
	if (type!=INTEGER) throw ErrorException(__FUNCT__,exprintf("%s%i"," trying to set integer for type",type));
	integer=value;
}

#undef __FUNCT__
#define __FUNCT__ "SetString"
void  Param::SetString(char* value){
	if (type!=STRING) throw ErrorException(__FUNCT__,exprintf("%s%i"," trying to set string for type",type));
	strcpy(string,value);
}

int   Param::GetM(){
	return M;
}

int   Param::GetN(){
	return N;
}

#undef __FUNCT__
#define __FUNCT__ "SetDoubleVec"
void  Param::SetDoubleVec(double* value,int size){
	if (type!=DOUBLEVEC) throw ErrorException(__FUNCT__,exprintf("%s%i"," trying to set doublevecfor type",type));
	
	M=size;
	doublevec=(double*)xmalloc(M*sizeof(double));
	memcpy(doublevec,value,M*sizeof(double));

}
