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

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

#include "../classes.h"
#include "../../shared/shared.h"
#include "./SegInput2.h"

/*SegInput2 constructors and destructor*/
SegInput2::SegInput2(void){/*{{{*/

	this->numberofelements_local = -1;
	this->numberofvertices_local = -1;
	this->isserved       = false;
	this->M = -1;
	this->N = -1;
	this->values         = NULL;
	this->element_values = NULL;

}/*}}}*/
SegInput2::SegInput2(int nbe_in,int nbv_in,int interp_in){/*{{{*/

	_assert_(nbe_in>0);
	_assert_(nbe_in<1e11);
	_assert_(nbv_in>0);
	_assert_(nbv_in<1e11);
	this->numberofelements_local = nbe_in;
	this->numberofvertices_local = nbv_in;
	this->isserved       = false;

	/*Reset takes care of the rest*/
	this->Reset(interp_in);
}/*}}}*/
SegInput2::~SegInput2(){/*{{{*/
	if(this->element_values) xDelete<IssmDouble>(this->element_values);
	if(this->values)         xDelete<IssmDouble>(this->values);
}
/*}}}*/
void SegInput2::Reset(int interp_in){/*{{{*/

	/*Clean up*/
	if(this->values)         xDelete<IssmDouble>(this->values);
	if(this->element_values) xDelete<IssmDouble>(this->element_values);

	/*Set interpolation*/
	this->interpolation  = interp_in;

	/*Create Sizes*/
	if(this->interpolation==P1Enum){
		this->M = this->numberofvertices_local;
		this->N = 1;
	}
	else{
		this->M = this->numberofelements_local;
		this->N = SegRef::NumberofNodes(interp_in);
	}

	/*Allocate Pointers*/
	this->values         = xNewZeroInit<IssmDouble>(this->M*this->N);
	this->element_values = xNewZeroInit<IssmDouble>(SegRef::NumberofNodes(interp_in));
}/*}}}*/

/*Object virtual functions definitions:*/
Input2* SegInput2::copy() {/*{{{*/

	SegInput2* output = new SegInput2(this->numberofelements_local,this->numberofvertices_local,this->interpolation);

	xMemCpy<IssmDouble>(output->values,this->values,this->M*this->N);
	xMemCpy<IssmDouble>(output->element_values,this->element_values,SegRef::NumberofNodes(this->interpolation));

	return output;
}
/*}}}*/
void SegInput2::DeepEcho(void){/*{{{*/
	_printf_("SegInput2 Echo:\n");
	_printf_("   interpolation: "<<EnumToStringx(this->interpolation)<<"\n");
	_printf_("   Size:          "<<M<<"x"<<N<<"\n");
	_printf_("   isserved:      "<<(isserved?"true":"false") << "\n");
	if(isserved){
		_printf_("   current values:      ");
		for(int i=0;i<3;i++) _printf_(" "<<this->element_values[i]);
		_printf_("] ("<<EnumToStringx(this->interpolation)<<")\n");
	}
	printarray(this->values,this->M,this->N);
	//_printf_(setw(15)<<"   SegInput2 "<<setw(25)<<left<<EnumToStringx(this->enum_type)<<" "<<(value?"true":"false") << "\n");
}
/*}}}*/
void SegInput2::Echo(void){/*{{{*/
	_printf_("SegInput2 Echo:\n");
	_printf_("   interpolation: "<<EnumToStringx(this->interpolation)<<"\n");
	_printf_("   Size:          "<<M<<"x"<<N<<"\n");
	_printf_("   isserved:      "<<(isserved?"true":"false") << "\n");
	if(isserved){
		_printf_("   current values:      ");
		_printf_("[ ");
		for(int i=0;i<3;i++) _printf_(" "<<this->element_values[i]);
		_printf_("] ("<<EnumToStringx(this->interpolation)<<")\n");
	}
}
/*}}}*/
int  SegInput2::Id(void){/*{{{*/
	return -1;
}/*}}}*/
void SegInput2::Marshall(char** pmarshalled_data,int* pmarshalled_data_size, int marshall_direction){ /*{{{*/

	MARSHALLING_ENUM(SegInput2Enum);
	MARSHALLING(this->numberofelements_local);
	MARSHALLING(this->numberofvertices_local);
	MARSHALLING(this->interpolation);
	MARSHALLING(this->M);
	MARSHALLING(this->N);
	this->isserved = false;
	if(this->M*this->N){
		MARSHALLING_DYNAMIC(this->values,IssmDouble,this->M*this->N);
	}
	else this->values = NULL;

	if(marshall_direction == MARSHALLING_BACKWARD){
		this->element_values = xNewZeroInit<IssmDouble>(SegRef::NumberofNodes(this->interpolation));
	}

}
/*}}}*/
int  SegInput2::ObjectEnum(void){/*{{{*/
	return SegInput2Enum;
}
/*}}}*/

/*SegInput2 management*/
void SegInput2::SetInput(int interp_in,int row,IssmDouble value_in){/*{{{*/

	_assert_(this);
	_assert_(row>=0); 
	_assert_(row<this->M); 
	_assert_(this->N==1);

	this->values[row] = value_in;
	this->isserved = false;
}
/*}}}*/
void SegInput2::SetInput(int interp_in,int numindices,int* indices,IssmDouble* values_in){/*{{{*/

	_assert_(this);
	if(interp_in==P1Enum && this->interpolation==P1Enum){
		_assert_(this->N==1);
		for(int i=0;i<numindices;i++){
			int row = indices[i];
			_assert_(row>=0); 
			_assert_(row<this->M); 
			this->values[row] = values_in[i];
		}
	}
	else if(interp_in==P0Enum && this->interpolation==P0Enum){
		_assert_(this->N==1);
		for(int i=0;i<numindices;i++){
			int row = indices[i];
			_assert_(row>=0); 
			_assert_(row<this->M); 
			this->values[row] = values_in[i];
		}
	}
	else if(this->interpolation!=P1Enum && interp_in==P1Enum){
		this->Reset(interp_in);
		for(int i=0;i<numindices;i++){
			int row = indices[i];
			_assert_(row>=0); 
			_assert_(row<this->M); 
			this->values[row] = values_in[i];
		}
	}
	else{
		_error_("Cannot convert "<<EnumToStringx(this->interpolation)<<" to "<<EnumToStringx(interp_in));
	}
	this->isserved = false;
}
/*}}}*/
void SegInput2::SetInput(int interp_in,int row,int numindices,IssmDouble* values_in){/*{{{*/

	_assert_(this);
	if(interp_in==this->interpolation){
		_assert_(this->N==numindices);
	}
	else{
		this->Reset(interp_in);
		_assert_(this->N==numindices);
	}
	for(int i=0;i<numindices;i++) this->values[row*this->N+i] = values_in[i];
	this->isserved = false;
}
/*}}}*/
void SegInput2::Serve(int numindices,int* indices){/*{{{*/

	_assert_(this);
	_assert_(this->N==1);

	for(int i=0;i<numindices;i++){
		int row = indices[i];
		_assert_(row>=0); 
		_assert_(row<this->M); 
		this->element_values[i] = this->values[row];
	}

	/*Set input as served*/
	this->isserved = true;
}
/*}}}*/
void SegInput2::Serve(int row,int numindices){/*{{{*/

	_assert_(this);
	_assert_(this->N==numindices);
	_assert_(row<this->M);
	_assert_(row>=0);

	for(int i=0;i<numindices;i++){
		this->element_values[i] = this->values[row*this->N+i];
	}

	/*Set input as served*/
	this->isserved = true;
} /*}}}*/
int  SegInput2::GetInterpolation(){/*{{{*/
	return this->interpolation;
}/*}}}*/
void SegInput2::GetInputAverage(IssmDouble* pvalue){/*{{{*/
	_assert_(this);
	_assert_(this->isserved);

	int        numnodes  = this->NumberofNodes(this->interpolation);
	IssmDouble numnodesd = reCast<int,IssmDouble>(numnodes);
	IssmDouble value     = 0.;

	for(int i=0;i<numnodes;i++) value+=this->element_values[i];
	value = value/numnodesd;

	*pvalue=value;
}/*}}}*/
IssmDouble SegInput2::GetInputMin(void){/*{{{*/
	_assert_(this);
	_assert_(this->isserved);

	int        numnodes  = this->NumberofNodes(this->interpolation);
	IssmDouble min=this->element_values[0];

	for(int i=1;i<numnodes;i++){
		if(this->element_values[i]<min) min=this->element_values[i];
	}
	return min;
}/*}}}*/
IssmDouble SegInput2::GetInputMax(void){/*{{{*/
	_assert_(this);
	_assert_(this->isserved);

	int        numnodes  = this->NumberofNodes(this->interpolation);
	IssmDouble max=this->element_values[0];

	for(int i=1;i<numnodes;i++){
		if(this->element_values[i]>max) max=this->element_values[i];
	}
	return max;
}/*}}}*/
IssmDouble SegInput2::GetInputMaxAbs(void){/*{{{*/
	_assert_(this);
	_assert_(this->isserved);

	int        numnodes  = this->NumberofNodes(this->interpolation);
	IssmDouble maxabs=fabs(this->element_values[0]);

	for(int i=1;i<numnodes;i++){
		if(fabs(this->element_values[i])>maxabs) maxabs=fabs(this->element_values[i]);
	}
	return maxabs;
}/*}}}*/
void SegInput2::GetInputDerivativeValue(IssmDouble* derivativevalues, IssmDouble* xyz_list, Gauss* gauss){/*{{{*/
	_assert_(this);
	_assert_(this->isserved);
	_assert_(gauss->Enum()==GaussSegEnum);
	SegRef::GetInputDerivativeValue(derivativevalues,this->element_values,xyz_list,(GaussSeg*)gauss,this->interpolation);
}/*}}}*/
void SegInput2::GetInputValue(IssmDouble* pvalue,Gauss* gauss){/*{{{*/
	_assert_(this);
	_assert_(this->isserved);
	_assert_(gauss->Enum()==GaussSegEnum);
	SegRef::GetInputValue(pvalue,this->element_values,(GaussSeg*)gauss,this->interpolation);
}/*}}}*/
int  SegInput2::GetResultArraySize(void){/*{{{*/
	return 1;
}
/*}}}*/
int  SegInput2::GetResultInterpolation(void){/*{{{*/
	if(this->interpolation==P0Enum || this->interpolation==P0DGEnum){
		return P0Enum;
	}
	return P1Enum;
}/*}}}*/
int  SegInput2::GetResultNumberOfNodes(void){/*{{{*/
	return this->N;
}
/*}}}*/
void SegInput2::Scale(IssmDouble alpha){/*{{{*/

	for(int i=0;i<this->M*this->N;i++) this->values[i] = alpha*this->values[i];
	for(int i=0;i<SegRef::NumberofNodes(this->interpolation);i++) this->element_values[i] = alpha*this->element_values[i];
}
/*}}}*/
void SegInput2::AXPY(Input2* xinput,IssmDouble alpha){/*{{{*/

	/*xinput is of the same type, so cast it: */
	if(xinput->ObjectEnum()!=SegInput2Enum) _error_("Operation not permitted because xinput is of type " << EnumToStringx(xinput->ObjectEnum()));
	SegInput2* xtriainput=xDynamicCast<SegInput2*>(xinput);
	if(xtriainput->GetInterpolation()!=this->interpolation) _error_("Operation not permitted because xinput is of type " << EnumToStringx(xinput->ObjectEnum()));

	/*Carry out the AXPY operation depending on type:*/
	for(int i=0;i<this->M*this->N;i++) this->values[i] = alpha*xtriainput->values[i] + this->values[i];
	for(int i=0;i<SegRef::NumberofNodes(this->interpolation);i++) this->element_values[i] = alpha*xtriainput->element_values[i] + this->element_values[i];
}
/*}}}*/

/*Object functions*/
