/*!\file ControlInput.c
 * \brief: implementation of the ControlInput 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 "../objects.h"
#include "../../EnumDefinitions/EnumDefinitions.h"
#include "../../shared/shared.h"
#include "../../Container/Container.h"
#include "../../include/include.h"

/*ControlInput constructors and destructor*/
/*FUNCTION ControlInput::ControlInput(){{{1*/
ControlInput::ControlInput(){
	control_id  = 0;
	values      = NULL;
	savedvalues = NULL;
	minvalues   = NULL;
	maxvalues   = NULL;
	gradient    = NULL;
}
/*}}}*/
/*FUNCTION ControlInput::ControlInput(int enum_type,int enum_input,double* pvalues,double* pmin,double* pmax,int id){{{1*/
ControlInput::ControlInput(int in_enum_type,int enum_input,double* pvalues,double* pmin,double* pmax,int id){

	control_id=id;
	enum_type=in_enum_type;

	switch(enum_input){
		case TriaP1InputEnum:
			values     =new TriaP1Input(enum_type,pvalues);
			savedvalues=new TriaP1Input(enum_type,pvalues);
			minvalues  =new TriaP1Input(enum_type,pmin);
			maxvalues  =new TriaP1Input(enum_type,pmax);
			break;
		case PentaP1InputEnum:
			values     =new PentaP1Input(enum_type,pvalues);
			savedvalues=new PentaP1Input(enum_type,pvalues);
			minvalues  =new PentaP1Input(enum_type,pmin);
			maxvalues  =new PentaP1Input(enum_type,pmax);
			break;
		default:
			_error_("Input of Enum %s not supported yet by ControlInput",EnumToStringx(enum_input));
	}
	gradient   =NULL;
}
/*}}}*/
/*FUNCTION ControlInput::~ControlInput(){{{1*/
ControlInput::~ControlInput(){
	delete values;
	delete savedvalues;
	delete minvalues;
	delete maxvalues;
	delete gradient;
}
/*}}}*/

/*Object virtual functions definitions:*/
		/*FUNCTION ControlInput::Echo {{{1*/
void ControlInput::Echo(void){
	this->DeepEcho();
}
/*}}}*/
/*FUNCTION ControlInput::DeepEcho{{{1*/
void ControlInput::DeepEcho(void){

	printf("ControlInput:\n");
	printf("   enum: %i (%s)\n",this->enum_type,EnumToStringx(this->enum_type));
	printf("---values: \n");     if (values)      values->Echo();
	printf("---savedvalues: \n");if (savedvalues) savedvalues->Echo();
	printf("---minvalues: \n");  if (minvalues)   minvalues->Echo();
	printf("---maxvalues: \n");  if (maxvalues)   maxvalues->Echo();
	printf("---gradient: \n");   if (gradient)    gradient->Echo();
}
/*}}}*/
/*FUNCTION ControlInput::Id{{{1*/
int    ControlInput::Id(void){ return -1; }
/*}}}*/
/*FUNCTION ControlInput::MyRank{{{1*/
int    ControlInput::MyRank(void){ 
	extern int my_rank;
	return my_rank; 
}
/*}}}*/
#ifdef _SERIAL_
/*FUNCTION ControlInput::Marshall{{{1*/
void  ControlInput::Marshall(char** pmarshalled_dataset){

	char* marshalled_dataset=NULL;
	int   enum_value=0;
	int   flag;

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

	/*get enum value of ControlInput: */
	enum_value=ControlInputEnum;
	
	/*marshall enum: */
	memcpy(marshalled_dataset,&enum_value,sizeof(enum_value));marshalled_dataset+=sizeof(enum_value);
	
	/*marshall enum_type: */
	memcpy(marshalled_dataset,&enum_type,sizeof(enum_type));marshalled_dataset+=sizeof(enum_type);
	memcpy(marshalled_dataset,&control_id,sizeof(control_id));marshalled_dataset+=sizeof(control_id);

	/*marshal values*/
	if(!values){
		flag=0;
		memcpy(marshalled_dataset,&flag,sizeof(flag));marshalled_dataset+=sizeof(flag);
	}
	else{
		flag=1;
		memcpy(marshalled_dataset,&flag,sizeof(flag));marshalled_dataset+=sizeof(flag);
		this->values->Marshall(&marshalled_dataset);
	}

	/*marshal savedvalues*/
	if(!savedvalues){
		flag=0;
		memcpy(marshalled_dataset,&flag,sizeof(flag));marshalled_dataset+=sizeof(flag);
	}
	else{
		flag=1;
		memcpy(marshalled_dataset,&flag,sizeof(flag));marshalled_dataset+=sizeof(flag);
		this->savedvalues->Marshall(&marshalled_dataset);
	}

	/*marshal minvalues*/
	if(!minvalues){
		flag=0;
		memcpy(marshalled_dataset,&flag,sizeof(flag));marshalled_dataset+=sizeof(flag);
	}
	else{
		flag=1;
		memcpy(marshalled_dataset,&flag,sizeof(flag));marshalled_dataset+=sizeof(flag);
		this->minvalues->Marshall(&marshalled_dataset);
	}

	/*marshal maxvalues*/
	if(!maxvalues){
		flag=0;
		memcpy(marshalled_dataset,&flag,sizeof(flag));marshalled_dataset+=sizeof(flag);
	}
	else{
		flag=1;
		memcpy(marshalled_dataset,&flag,sizeof(flag));marshalled_dataset+=sizeof(flag);
		this->maxvalues->Marshall(&marshalled_dataset);
	}

	/*marshal gradient*/
	if(!gradient){
		flag=0;
		memcpy(marshalled_dataset,&flag,sizeof(flag));marshalled_dataset+=sizeof(flag);
	}
	else{
		flag=1;
		memcpy(marshalled_dataset,&flag,sizeof(flag));marshalled_dataset+=sizeof(flag);
		this->gradient->Marshall(&marshalled_dataset);
	}

	/*clean up and assign output pointer*/
	*pmarshalled_dataset=marshalled_dataset;
}
/*}}}*/
/*FUNCTION ControlInput::MarshallSize{{{1*/
int   ControlInput::MarshallSize(){
	
	int size=0;

	size=sizeof(enum_type)+
	  +sizeof(control_id)
	  +5*sizeof(int) //5 flags
	  +sizeof(int); //sizeof(int) for enum value

	if(values)     size+=values->MarshallSize();
	if(savedvalues)size+=savedvalues->MarshallSize();
	if(minvalues)size+=minvalues->MarshallSize();
	if(maxvalues)size+=maxvalues->MarshallSize();
	if(gradient)   size+=gradient->MarshallSize();
	return size;
}
/*}}}*/
/*FUNCTION ControlInput::Demarshall{{{1*/
void  ControlInput::Demarshall(char** pmarshalled_dataset){

	char* marshalled_dataset=NULL;
	int   flag,input_enum_type;

	/*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(&enum_type,marshalled_dataset,sizeof(enum_type));marshalled_dataset+=sizeof(enum_type);
	memcpy(&control_id,marshalled_dataset,sizeof(control_id));marshalled_dataset+=sizeof(control_id);

	/*Demarshal values*/
	memcpy(&flag,marshalled_dataset,sizeof(int));marshalled_dataset+=sizeof(int);
	if(flag){
		memcpy(&input_enum_type,marshalled_dataset,sizeof(int)); marshalled_dataset+=sizeof(int);
		if(input_enum_type==PentaP1InputEnum){
			values=new PentaP1Input();
			values->Demarshall(&marshalled_dataset);
		}
		else if(input_enum_type==TriaP1InputEnum){
			values=new TriaP1Input();
			values->Demarshall(&marshalled_dataset);
		}
		else _error_("Not supported yet");
	}
	else{
		values=NULL;
	}

	/*Demarshal savedvalues*/
	memcpy(&flag,marshalled_dataset,sizeof(int));marshalled_dataset+=sizeof(int);
	if(flag){
		memcpy(&input_enum_type,marshalled_dataset,sizeof(int)); marshalled_dataset+=sizeof(int);
		if(input_enum_type==PentaP1InputEnum){
			savedvalues=new PentaP1Input();
			savedvalues->Demarshall(&marshalled_dataset);
		}
		else if(input_enum_type==TriaP1InputEnum){
			savedvalues=new TriaP1Input();
			savedvalues->Demarshall(&marshalled_dataset);
		}
		else _error_("Not supported yet");
	}
	else{
		savedvalues=NULL;
	}

	/*Demarshal minvalues*/
	memcpy(&flag,marshalled_dataset,sizeof(int));marshalled_dataset+=sizeof(int);
	if(flag){
		memcpy(&input_enum_type,marshalled_dataset,sizeof(int)); marshalled_dataset+=sizeof(int);
		if(input_enum_type==PentaP1InputEnum){
			minvalues=new PentaP1Input();
			minvalues->Demarshall(&marshalled_dataset);
		}
		else if(input_enum_type==TriaP1InputEnum){
			minvalues=new TriaP1Input();
			minvalues->Demarshall(&marshalled_dataset);
		}
		else _error_("Not supported yet");
	}
	else{
		minvalues=NULL;
	}

	/*Demarshal maxvalues*/
	memcpy(&flag,marshalled_dataset,sizeof(int));marshalled_dataset+=sizeof(int);
	if(flag){
		memcpy(&input_enum_type,marshalled_dataset,sizeof(int)); marshalled_dataset+=sizeof(int);
		if(input_enum_type==PentaP1InputEnum){
			maxvalues=new PentaP1Input();
			maxvalues->Demarshall(&marshalled_dataset);
		}
		else if(input_enum_type==TriaP1InputEnum){
			maxvalues=new TriaP1Input();
			maxvalues->Demarshall(&marshalled_dataset);
		}
		else _error_("Not supported yet");
	}
	else{
		maxvalues=NULL;
	}

	/*Demarshal gradient*/
	memcpy(&flag,marshalled_dataset,sizeof(int));marshalled_dataset+=sizeof(int);
	if(flag){
		memcpy(&input_enum_type,marshalled_dataset,sizeof(int)); marshalled_dataset+=sizeof(int);
		if(input_enum_type==PentaP1InputEnum){
			gradient=new PentaP1Input();
			gradient->Demarshall(&marshalled_dataset);
		}
		else if(input_enum_type==TriaP1InputEnum){
			gradient=new TriaP1Input();
			gradient->Demarshall(&marshalled_dataset);
		}
		else _error_("Not supported yet");
	}
	else{
		gradient=NULL;
	}

	/*return: */
	*pmarshalled_dataset=marshalled_dataset;
	return;
}
/*}}}*/
#endif
/*FUNCTION ControlInput::ObjectEnum{{{1*/
int ControlInput::ObjectEnum(void){

	return ControlInputEnum;

}
/*}}}*/
/*FUNCTION ControlInput::copy{{{1*/
Object* ControlInput::copy() {
	
	ControlInput* output=NULL;

	output = new ControlInput();
	output->enum_type=this->enum_type;
	output->control_id=this->control_id;

	if(values)      output->values=(Input*)this->values->copy();
	if(savedvalues) output->savedvalues=(Input*)this->savedvalues->copy();
	if(minvalues)   output->minvalues=(Input*)this->minvalues->copy();
	if(maxvalues)   output->maxvalues=(Input*)this->maxvalues->copy();
	if(gradient)    output->gradient=(Input*)this->gradient->copy();

	return output;
}
/*}}}*/
	
/*ControlInput management*/
/*FUNCTION ControlInput::InstanceEnum{{{1*/
int ControlInput::InstanceEnum(void){

	return this->enum_type;

}
/*}}}*/

/*Object functions*/
/*FUNCTION ControlInput::Constrain(){{{1*/
void ControlInput::Constrain(void){

	Input* newvalues=NULL;

	newvalues=this->values->PointwiseMin(maxvalues);
	delete values; this->values=newvalues;
	newvalues=this->values->PointwiseMax(minvalues);
	delete values; this->values=newvalues;
}/*}}}*/
/*FUNCTION ControlInput::Constrain(double min, double max){{{1*/
void ControlInput::Constrain(double min, double max){
	   values->Constrain(min,max);
}/*}}}*/
/*FUNCTION ControlInput::Extrude{{{1*/
void ControlInput::Extrude(void){
	values->Extrude();
	savedvalues->Extrude();
	//gradient->Extrude();
}/*}}}*/
/*FUNCTION ControlInput::GetGradient{{{1*/
void ControlInput::GetGradient(Vec gradient_vec,int* doflist){
	if(gradient) gradient->GetVectorFromInputs(gradient_vec,doflist);
}/*}}}*/
/*FUNCTION ControlInput::ScaleGradient{{{1*/
void ControlInput::ScaleGradient(double scaling_factor){
	if(!gradient) _error_("Gradient of ControlInput %s not found",EnumToStringx(enum_type));
	gradient->Scale(scaling_factor);
}/*}}}*/
/*FUNCTION ControlInput::SetGradient{{{1*/
void ControlInput::SetGradient(Input* gradient_in){

	/*Get enum for current gradient*/
	switch(this->control_id){
		case 1:
			gradient_in->ChangeEnum(Gradient1Enum);
			break;
		case 2:
			gradient_in->ChangeEnum(Gradient2Enum);
			break;
		case 3:
			gradient_in->ChangeEnum(Gradient3Enum);
			break;
		default:
			_error_("more than 3 controls not implemented yet (Gradient %i was requested). EnumDefinitions.h needs to be updated.",this->control_id);
	}

	/*Delete old gradient and assign new gradient*/
	if(gradient) delete gradient;
	gradient=gradient_in;

}/*}}}*/
/*FUNCTION ControlInput::SetInput{{{1*/
void ControlInput::SetInput(Input* in_input){

	delete values; this->values=in_input;
	this->SaveValue(); //because this is what SpawnResult saves FIXME

}/*}}}*/
/*FUNCTION ControlInput::SpawnResult{{{1*/
ElementResult* ControlInput::SpawnResult(int step, double time){
	return savedvalues->SpawnResult(step,time);
}/*}}}*/
/*FUNCTION ControlInput::SpawnTriaInput{{{1*/
Input* ControlInput::SpawnTriaInput(int* indices){
	return values->SpawnTriaInput(indices);
}/*}}}*/
/*FUNCTION ControlInput::SpawnGradient{{{1*/
ElementResult* ControlInput::SpawnGradient(int step, double time){
	_assert_(gradient);
	return gradient->SpawnResult(step,time);
}/*}}}*/
/*FUNCTION ControlInput::GetVectorFromInputs(Vec vector,int* doflist){{{1*/
void ControlInput::GetVectorFromInputs(Vec vector,int* doflist){
	values->GetVectorFromInputs(vector,doflist);
}/*}}}*/
/*FUNCTION ControlInput::GetVectorFromInputs(Vec vector,int* doflist,const char* data){{{1*/
void ControlInput::GetVectorFromInputs(Vec vector,int* doflist,const char* data){
	 if(strcmp(data,"value")==0){
		 _assert_(values);
		 values->GetVectorFromInputs(vector,doflist);
	 }
	 else if (strcmp(data,"lowerbound")==0){
		 _assert_(minvalues);
		 minvalues->GetVectorFromInputs(vector,doflist);
	 }
	 else if (strcmp(data,"upperbound")==0){
		 _assert_(maxvalues);
		 maxvalues->GetVectorFromInputs(vector,doflist);
	 }
	 else if (strcmp(data,"gradient")==0){
		 _assert_(gradient);
		 gradient->GetVectorFromInputs(vector,doflist);
	 }
	 else{
		 _error_("Data %s not supported yet",data);
	 }
}/*}}}*/
/*FUNCTION ControlInput::GetInputAverage(double* pvalue){{{1*/
void ControlInput::GetInputAverage(double* pvalue){
	values->GetInputAverage(pvalue);
}/*}}}*/
/*FUNCTION ControlInput::GetInputValue(bool* pvalue){{{1*/
void ControlInput::GetInputValue(bool* pvalue){
	values->GetInputValue(pvalue);
}/*}}}*/
/*FUNCTION ControlInput::GetInputValue(int* pvalue){{{1*/
void ControlInput::GetInputValue(int* pvalue){
	values->GetInputValue(pvalue);
}/*}}}*/
/*FUNCTION ControlInput::GetInputValue(double* pvalue){{{1*/
void ControlInput::GetInputValue(double* pvalue){
	values->GetInputValue(pvalue);
}/*}}}*/
/*FUNCTION ControlInput::GetInputValue(double* pvalue){{{1*/
void ControlInput::GetInputValue(double* pvalue,GaussTria* gauss){
	values->GetInputValue(pvalue,gauss);
}/*}}}*/
/*FUNCTION ControlInput::GetInputValue(double* pvalue){{{1*/
void ControlInput::GetInputValue(double* pvalue,GaussPenta* gauss){
	values->GetInputValue(pvalue,gauss);
}/*}}}*/
/*FUNCTION ControlInput::GetInputDerivativeValue(double* derivativevalues, double* xyz_list, GaussTria* gauss){{{1*/
void ControlInput::GetInputDerivativeValue(double* derivativevalues, double* xyz_list, GaussTria* gauss){
	values->GetInputDerivativeValue(derivativevalues,xyz_list,gauss);
}/*}}}*/
/*FUNCTION ControlInput::GetInputDerivativeValue(double* derivativevalues, double* xyz_list, GaussPenta* gauss){{{1*/
void ControlInput::GetInputDerivativeValue(double* derivativevalues, double* xyz_list, GaussPenta* gauss){
	values->GetInputDerivativeValue(derivativevalues,xyz_list,gauss);
}/*}}}*/
/*FUNCTION ControlInput::SaveValue{{{1*/
void ControlInput::SaveValue(void){
	if(!values) _error_("Values of %s not found",EnumToStringx(this->enum_type));

	if(savedvalues) delete this->savedvalues;
	this->savedvalues=(Input*)this->values->copy();
}/*}}}*/
/*FUNCTION ControlInput::UpdateValue{{{1*/
void ControlInput::UpdateValue(double scalar){
	if(!gradient)    _error_("Gradient of %s not found",EnumToStringx(this->enum_type));
	if(!savedvalues) _error_("Values of %s not found",EnumToStringx(this->enum_type));

	if(values) delete this->values;
	this->values=(Input*)this->savedvalues->copy();
	this->values->AXPY(gradient,scalar);
}/*}}}*/
/*FUNCTION ControlInput::VerticallyIntegrate{{{1*/
void ControlInput::VerticallyIntegrate(Input* thickness_input){
	values->VerticallyIntegrate(thickness_input);
}/*}}}*/
/*FUNCTION ControlInput::Configure{{{1*/
void ControlInput::Configure(Parameters* parameters){
	/*do nothing: */
}
/*}}}*/
