/*!\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(){{{*/
ControlInput::ControlInput(){
	control_id  = 0;
	values      = NULL;
	savedvalues = NULL;
	minvalues   = NULL;
	maxvalues   = NULL;
	gradient    = NULL;
}
/*}}}*/
/*FUNCTION ControlInput::ControlInput(int enum_type,int enum_input,IssmDouble* pvalues,IssmDouble* pmin,IssmDouble* pmax,int id){{{*/
ControlInput::ControlInput(int in_enum_type,int enum_input,IssmDouble* pvalues,IssmDouble* pmin,IssmDouble* 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:
			_error2_("Input of Enum " << EnumToStringx(enum_input) << " not supported yet by ControlInput");
	}
	gradient   =NULL;
}
/*}}}*/
/*FUNCTION ControlInput::~ControlInput(){{{*/
ControlInput::~ControlInput(){
	delete values;
	delete savedvalues;
	delete minvalues;
	delete maxvalues;
	delete gradient;
}
/*}}}*/

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

	_printLine_("ControlInput:");
	_printLine_("   enum: " << this->enum_type << " (" << EnumToStringx(this->enum_type) << ")");
	_printLine_("---values: ");     if (values)      values->Echo();
	_printLine_("---savedvalues: ");if (savedvalues) savedvalues->Echo();
	_printLine_("---minvalues: ");  if (minvalues)   minvalues->Echo();
	_printLine_("---maxvalues: ");  if (maxvalues)   maxvalues->Echo();
	_printLine_("---gradient: ");   if (gradient)    gradient->Echo();
}
/*}}}*/
/*FUNCTION ControlInput::Id{{{*/
int    ControlInput::Id(void){ return -1; }
/*}}}*/
/*FUNCTION ControlInput::MyRank{{{*/
int    ControlInput::MyRank(void){ 
	extern int my_rank;
	return my_rank; 
}
/*}}}*/
/*FUNCTION ControlInput::ObjectEnum{{{*/
int ControlInput::ObjectEnum(void){

	return ControlInputEnum;

}
/*}}}*/
/*FUNCTION ControlInput::copy{{{*/
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{{{*/
int ControlInput::InstanceEnum(void){

	return this->enum_type;

}
/*}}}*/

/*Object functions*/
/*FUNCTION ControlInput::Constrain(){{{*/
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(IssmDouble min, IssmDouble max){{{*/
void ControlInput::Constrain(IssmDouble min, IssmDouble max){
	   values->Constrain(min,max);
}/*}}}*/
/*FUNCTION ControlInput::Extrude{{{*/
void ControlInput::Extrude(void){
	values->Extrude();
	savedvalues->Extrude();
	//gradient->Extrude();
}/*}}}*/
/*FUNCTION ControlInput::GetGradient{{{*/
void ControlInput::GetGradient(Vector* gradient_vec,int* doflist){
	if(gradient) gradient->GetVectorFromInputs(gradient_vec,doflist);
}/*}}}*/
/*FUNCTION ControlInput::ScaleGradient{{{*/
void ControlInput::ScaleGradient(IssmDouble scaling_factor){
	if(!gradient) _error2_("Gradient of ControlInput " << EnumToStringx(enum_type) << " not found");
	gradient->Scale(scaling_factor);
}/*}}}*/
/*FUNCTION ControlInput::SetGradient{{{*/
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:
			_error2_("more than 3 controls not implemented yet (Gradient " << this->control_id << " was requested). EnumDefinitions.h needs to be updated.");
	}

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

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

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

}/*}}}*/
/*FUNCTION ControlInput::SpawnResult{{{*/
ElementResult* ControlInput::SpawnResult(int step, IssmDouble time){
	return savedvalues->SpawnResult(step,time);
}/*}}}*/
/*FUNCTION ControlInput::SpawnTriaInput{{{*/
Input* ControlInput::SpawnTriaInput(int* indices){
	return values->SpawnTriaInput(indices);
}/*}}}*/
/*FUNCTION ControlInput::SpawnGradient{{{*/
ElementResult* ControlInput::SpawnGradient(int step, IssmDouble time){
	_assert_(gradient);
	return gradient->SpawnResult(step,time);
}/*}}}*/
/*FUNCTION ControlInput::GetVectorFromInputs(Vector* vector,int* doflist){{{*/
void ControlInput::GetVectorFromInputs(Vector* vector,int* doflist){
	values->GetVectorFromInputs(vector,doflist);
}/*}}}*/
/*FUNCTION ControlInput::GetVectorFromInputs(Vector* vector,int* doflist,const char* data){{{*/
void ControlInput::GetVectorFromInputs(Vector* 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{
		 _error2_("Data " << data << " not supported yet");
	 }
}/*}}}*/
/*FUNCTION ControlInput::GetInputAverage(IssmDouble* pvalue){{{*/
void ControlInput::GetInputAverage(IssmDouble* pvalue){
	values->GetInputAverage(pvalue);
}/*}}}*/
/*FUNCTION ControlInput::GetInputValue(bool* pvalue){{{*/
void ControlInput::GetInputValue(bool* pvalue){
	values->GetInputValue(pvalue);
}/*}}}*/
/*FUNCTION ControlInput::GetInputValue(int* pvalue){{{*/
void ControlInput::GetInputValue(int* pvalue){
	values->GetInputValue(pvalue);
}/*}}}*/
/*FUNCTION ControlInput::GetInputValue(IssmDouble* pvalue){{{*/
void ControlInput::GetInputValue(IssmDouble* pvalue){
	values->GetInputValue(pvalue);
}/*}}}*/
/*FUNCTION ControlInput::GetInputValue(IssmDouble* pvalue){{{*/
void ControlInput::GetInputValue(IssmDouble* pvalue,GaussTria* gauss){
	values->GetInputValue(pvalue,gauss);
}/*}}}*/
/*FUNCTION ControlInput::GetInputValue(IssmDouble* pvalue){{{*/
void ControlInput::GetInputValue(IssmDouble* pvalue,GaussPenta* gauss){
	values->GetInputValue(pvalue,gauss);
}/*}}}*/
/*FUNCTION ControlInput::GetInputDerivativeValue(IssmDouble* derivativevalues, IssmDouble* xyz_list, GaussTria* gauss){{{*/
void ControlInput::GetInputDerivativeValue(IssmDouble* derivativevalues, IssmDouble* xyz_list, GaussTria* gauss){
	values->GetInputDerivativeValue(derivativevalues,xyz_list,gauss);
}/*}}}*/
/*FUNCTION ControlInput::GetInputDerivativeValue(IssmDouble* derivativevalues, IssmDouble* xyz_list, GaussPenta* gauss){{{*/
void ControlInput::GetInputDerivativeValue(IssmDouble* derivativevalues, IssmDouble* xyz_list, GaussPenta* gauss){
	values->GetInputDerivativeValue(derivativevalues,xyz_list,gauss);
}/*}}}*/
/*FUNCTION ControlInput::SaveValue{{{*/
void ControlInput::SaveValue(void){
	if(!values) _error2_("Values of " << EnumToStringx(this->enum_type) << " not found");

	if(savedvalues) delete this->savedvalues;
	this->savedvalues=(Input*)this->values->copy();
}/*}}}*/
/*FUNCTION ControlInput::UpdateValue{{{*/
void ControlInput::UpdateValue(IssmDouble scalar){
	if(!gradient)    _error2_("Gradient of " << EnumToStringx(this->enum_type) << " not found");
	if(!savedvalues) _error2_("Values of " << EnumToStringx(this->enum_type) << " not found");

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