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

/*SpcTransient constructors and destructor*/
/*FUNCTION SpcTransient::SpcTransient(){{{*/
SpcTransient::SpcTransient(){
	sid=-1;
	nodeid=-1;
	dof=-1;
	values=NULL;
	times=NULL;
	nsteps=-1;
	analysis_type=-1;
	return;
}
/*}}}*/
/*FUNCTION SpcTransient::SpcTransient(int spc_sid,int spc_nodeid,...){{{*/
SpcTransient::SpcTransient(int spc_sid,int spc_nodeid, int spc_dof,int spc_nsteps, IssmDouble* spc_times, IssmDouble* spc_values,int spc_analysis_type){

	sid=spc_sid;
	nodeid=spc_nodeid;
	dof=spc_dof;
	nsteps=spc_nsteps;
	if(spc_nsteps){
		values=xNew<IssmDouble>(spc_nsteps);
		times=xNew<IssmDouble>(spc_nsteps);
		xMemCpy<IssmDouble>(values,spc_values,nsteps);
		xMemCpy<IssmDouble>(times,spc_times,nsteps);
	}
	analysis_type=spc_analysis_type;
	return;
}
/*}}}*/
/*FUNCTION SpcTransient::~SpcTransient{{{*/
SpcTransient::~SpcTransient(){
	xDelete<IssmDouble>(times);
	xDelete<IssmDouble>(values);
	return;
}
/*}}}*/
		
/*Object virtual functions definitions:*/
/*FUNCTION SpcTransient::Echo {{{*/
void SpcTransient::Echo(void){

	int i;
	_printLine_("SpcTransient:");
	_printLine_("   sid: " << sid);
	_printLine_("   nodeid: " << nodeid);
	_printLine_("   dof: " << dof);
	_printLine_("   nsteps: " << nsteps);
	_printLine_("   analysis_type: " << EnumToStringx(analysis_type));
	_printLine_("   steps|times|values");
	for(i=0;i<nsteps;i++){
		_printLine_(i << "-" << times[i] << ":" << values[i]);
	}
	return;
}
/*}}}*/
/*FUNCTION SpcTransient::DeepEcho {{{*/
void SpcTransient::DeepEcho(void){
	this->Echo();
}		
/*}}}*/
/*FUNCTION SpcTransient::Id {{{*/
int    SpcTransient::Id(void){ return sid; }
/*}}}*/
/*FUNCTION SpcTransient::MyRank {{{*/
int    SpcTransient::MyRank(void){ 
	extern int my_rank;
	return my_rank; 
}
/*}}}*/
/*FUNCTION SpcTransient::ObjectEnum{{{*/
int SpcTransient::ObjectEnum(void){

	return SpcTransientEnum;

}
/*}}}*/
/*FUNCTION SpcTransient::copy {{{*/
Object* SpcTransient::copy() {
	return new SpcTransient(sid,nodeid,dof,nsteps,times,values,analysis_type);
}
/*}}}*/

/*Constraint virtual functions definitions:*/
/*FUNCTION SpcTransient::InAnalysis{{{*/
bool SpcTransient::InAnalysis(int in_analysis_type){
	
	if (in_analysis_type==this->analysis_type) return true;
	else return false;
}
/*}}}*/
/*FUNCTION SpcTransient::ConstrainNode{{{*/
void SpcTransient::ConstrainNode(Nodes* nodes,Parameters* parameters){

	Node* node=NULL;
	IssmDouble time=0;
	int    i;
	IssmDouble alpha=-1;
	IssmDouble value;
	bool   found=false;

	/*Chase through nodes and find the node to which this SpcTransient applys: */
	node=(Node*)nodes->GetObjectById(NULL,nodeid);
	
	if(node){ //in case the spc is dealing with a node on another cpu

		/*Retrieve time in parameters: */
		parameters->FindParam(&time,TimeEnum);

		/*Now, go fetch value for this time: */
		if (time<=times[0]){
			value=values[0];
			found=true;
		}
		else if (time>=times[nsteps-1]){
			value=values[nsteps-1];
			found=true;
		}
		else{
			for(i=0;i<nsteps-1;i++){
				if (times[i]<=time && time<times[i+1]){
					alpha=(time-times[i])/(times[i+1]-times[i]);
					value=(1-alpha)*values[i]+alpha*values[i+1];
					found=true;
					break;
				}
			}
		}

		if(!found)_error2_("could not find time segment for constraint");

		/*Apply or relax constraint: */
		if(xIsNan<IssmDouble>(value)){
			node->RelaxConstraint(dof);
		}
		else node->ApplyConstraint(dof,value);
	}
}
/*}}}*/

/*SpcTransient functions*/
/*FUNCTION SpcTransient::GetDof {{{*/
int SpcTransient::GetDof(){
	return dof;
}
/*}}}*/
/*FUNCTION SpcTransient::GetNodeId {{{*/
int   SpcTransient::GetNodeId(){
	
	return nodeid;
}
/*}}}*/
/*FUNCTION SpcTransient::GetValue {{{*/
IssmDouble SpcTransient::GetValue(){
	return values[0];
}
/*}}}*/

