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

		
Node::Node(){
	return;
}
Node::Node(int node_id,int node_partitionborder,int node_numdofs, double node_x[3],int node_onbed,int node_onsurface){

	int i;
	
	id=node_id;
	partitionborder=node_partitionborder;
	numberofdofs=node_numdofs;
	x[0]=node_x[0];
	x[1]=node_x[1];
	x[2]=node_x[2];
	onbed=node_onbed;
	onsurface=node_onsurface;

	/*Initialize sets: */
	for(i=0;i<numberofdofs;i++){
		mset[i]=0;
		nset[i]=1; 
		fset[i]=1; //we assume new nodes are not constrained in rigid body mode, or single point constraint mode.
		sset[i]=0;
	}

	return;
}

Node::~Node(){
	return;
}
		
void Node::Echo(void){

	int i;

	printf("Node:\n");
	printf("   id: %i\n",id);
	printf("   partitionborder: %i\n",partitionborder);
	printf("   clone: %i\n",clone);
	printf("   numberofdofs: %i\n",numberofdofs);
	printf("   x=[%g,%g,%g]\n",x[0],x[1],x[2]);
	printf("   onbed: %i\n",onbed);
	printf("   onsurface: %i\n",onsurface);
	printf("   doflist:|");
	for(i=0;i<numberofdofs;i++){
		if(i>MAXDOFSPERNODE)break;
		printf("%i|",doflist[i]);
	}
	printf("\n");

	printf("   set membership: m,n,f,s sets \n");
	for(i=0;i<numberofdofs;i++){
		if(i>MAXDOFSPERNODE)break;
		printf("      dof %i: %i %i %i %i\n",i,mset[i],nset[i],fset[i],sset[i]);
	}
	printf("\n");

	return;
}
		
void  Node::Marshall(char** pmarshalled_dataset){

	char* marshalled_dataset=NULL;
	int   enum_type=0;

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

	/*get enum type of Node: */
	enum_type=NodeEnum();
	
	/*marshall enum: */
	memcpy(marshalled_dataset,&enum_type,sizeof(enum_type));marshalled_dataset+=sizeof(enum_type);
	
	/*marshall Node data: */
	memcpy(marshalled_dataset,&id,sizeof(id));marshalled_dataset+=sizeof(id);
	memcpy(marshalled_dataset,&partitionborder,sizeof(partitionborder));marshalled_dataset+=sizeof(partitionborder);
	memcpy(marshalled_dataset,&clone,sizeof(clone));marshalled_dataset+=sizeof(clone);
	memcpy(marshalled_dataset,&numberofdofs,sizeof(numberofdofs));marshalled_dataset+=sizeof(numberofdofs);
	memcpy(marshalled_dataset,&x,sizeof(x));marshalled_dataset+=sizeof(x);
	memcpy(marshalled_dataset,&onbed,sizeof(onbed));marshalled_dataset+=sizeof(onbed);
	memcpy(marshalled_dataset,&onsurface,sizeof(onsurface));marshalled_dataset+=sizeof(onsurface);
	memcpy(marshalled_dataset,&doflist,sizeof(doflist));marshalled_dataset+=sizeof(doflist);
	memcpy(marshalled_dataset,&mset,sizeof(mset));marshalled_dataset+=sizeof(mset);
	memcpy(marshalled_dataset,&nset,sizeof(nset));marshalled_dataset+=sizeof(nset);
	memcpy(marshalled_dataset,&fset,sizeof(fset));marshalled_dataset+=sizeof(fset);
	memcpy(marshalled_dataset,&sset,sizeof(sset));marshalled_dataset+=sizeof(sset);

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

	return sizeof(id)+
		sizeof(partitionborder)+
		sizeof(clone)+
		sizeof(numberofdofs)+
		sizeof(x)+
		sizeof(onbed)+
		sizeof(onsurface)+
		sizeof(doflist)+
		sizeof(mset)+
		sizeof(nset)+
		sizeof(fset)+
		sizeof(sset)+
		sizeof(int); //sizeof(int) for enum type
}

char* Node::GetName(void){
	return "node";
}
		

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

	char* marshalled_dataset=NULL;

	/*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(&partitionborder,marshalled_dataset,sizeof(partitionborder));marshalled_dataset+=sizeof(partitionborder);
	memcpy(&clone,marshalled_dataset,sizeof(clone));marshalled_dataset+=sizeof(clone);
	memcpy(&numberofdofs,marshalled_dataset,sizeof(numberofdofs));marshalled_dataset+=sizeof(numberofdofs);
	memcpy(&x,marshalled_dataset,sizeof(x));marshalled_dataset+=sizeof(x);
	memcpy(&onbed,marshalled_dataset,sizeof(onbed));marshalled_dataset+=sizeof(onbed);
	memcpy(&onsurface,marshalled_dataset,sizeof(onsurface));marshalled_dataset+=sizeof(onsurface);
	memcpy(&doflist,marshalled_dataset,sizeof(doflist));marshalled_dataset+=sizeof(doflist);
	memcpy(&mset,marshalled_dataset,sizeof(mset));marshalled_dataset+=sizeof(mset);
	memcpy(&nset,marshalled_dataset,sizeof(nset));marshalled_dataset+=sizeof(nset);
	memcpy(&fset,marshalled_dataset,sizeof(fset));marshalled_dataset+=sizeof(fset);
	memcpy(&sset,marshalled_dataset,sizeof(sset));marshalled_dataset+=sizeof(sset);

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

int Node::Enum(void){

	return NodeEnum();

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

int    Node::MyRank(void){ 
	extern int my_rank;

	return my_rank; 
}

void  Node::DistributeDofs(int* pdofcount){ 

	int i;
	extern int my_rank;
	int dofcount;

	dofcount=*pdofcount;
	
	if(clone){
		/*This node is a clone! Don't distribute dofs, it will get them from another cpu!*/
		return;
	}

	/*This node should distribute dofs, go ahead: */
	for(i=0;i<numberofdofs;i++){
		doflist[i]=dofcount+i;
	}
	dofcount+=numberofdofs;

	/*Assign output pointers: */
	*pdofcount=dofcount;

	return; 
}

void  Node::UpdateDofs(int dofcount){
	
	int i;
	extern int my_rank;
	
	if(clone){
		/*This node is a clone, don't update the dofs!: */
		return;
	}

	/*This node should update the dofs, go ahead: */
	for(i=0;i<numberofdofs;i++){
		doflist[i]+=dofcount;
	}

	return; 
}

void  Node::ShowBorderDofs(int* borderdofs){

	int j;
	extern int my_rank;
	
	/*Is this node on the partition border? */
	if(!partitionborder)return;

	/*Are we the cpu that created this node's dof list? */
	if(clone)return;

	/*Ok, we are on the partition border, and we did create the 
	 * dofs for this node, plug the doflist into borderdofs: */
	for(j=0;j<numberofdofs;j++){
		*(borderdofs+numberofdofs*(id-1)+j)=doflist[j];
	}

	return;
}

void  Node::UpdateBorderDofs(int* allborderdofs){ 

	int j;
	extern int my_rank;
	
	/*Is this node on the partition border? */
	if(!partitionborder)return;
	
	/*Are we the cpu that created this node's dof list? */
	if(clone==0)return;

	/*Ok, we are on the partition border, but we did not create 
	 * the dofs for this node. Therefore, our doflist is garbage right 
	 * now. Go pick it up in the allborderdofs: */
	for(j=0;j<numberofdofs;j++){
		doflist[j]=*(allborderdofs+numberofdofs*(id-1)+j);
	}	
	return; 
}
void  Node::CreatePartition(Vec partition){ 

	int      i;
	int*     idxm=NULL;
	double*  values=NULL;

	idxm=(int*)xmalloc(numberofdofs*sizeof(int));
	values=(double*)xmalloc(numberofdofs*sizeof(double));

	for(i=0;i<numberofdofs;i++)idxm[i]=(id-1)*numberofdofs+i;
	for(i=0;i<numberofdofs;i++)values[i]=(double)doflist[i];

	VecSetValues(partition,numberofdofs,idxm,values,INSERT_VALUES);

	xfree((void**)&idxm);
	xfree((void**)&values);

	return;
}
void  Node::DistributeNumDofs(int* numdofspernode,int analysis_type){return;}
		
		
void  Node::SetClone(int* minranks){

	extern int my_rank;

	if (minranks[id-1]==my_rank){
		clone=0;
	}
	else{
		/*!there is a cpu with lower rank that has the same node, 
		therefore, I am a clone*/
		clone=1; 	
	}

}
		

int   Node::GetNumberOfDofs(){
	
	return numberofdofs;

}

int   Node::IsClone(){
	
	return clone;

}


void  Node::ApplyConstraint(Vec yg,int dof,double value){

	int index;

	/*First, dof should be added in the s set, describing which 
	 * dofs are constrained to a certain value (dirichlet boundary condition*/

	DofInSSet(dof-1);

	/*Second, we should add value into yg, at dof corresponding to doflist[dof], unless
	 *  we are a clone!*/

	if(!clone){

		index=doflist[dof-1]; //matlab indexing

		VecSetValues(yg,1,&index,&value,INSERT_VALUES);

	}

}
		

void  Node::DofInSSet(int dof){

	/*Put dof for this node into the s set (ie, this dof will be constrained 
	 * to a fixed value during computations. */

	mset[dof]=0; //m and n are mutually exclusive (m for rigid body modes)
	nset[dof]=1;
	fset[dof]=0; //n splits into f (for which we solve) and s (single point constraints)
	sset[dof]=1;
}

void  Node::DofInMSet(int dof){

	/*Put dof for this node into the m set (m set is for rigid body modes)*/

	mset[dof]=1; //m and n are mutually exclusive (m for rigid body modes)
	nset[dof]=0;
	fset[dof]=0; //n splits into f (for which we solve) and s (single point constraints)
	sset[dof]=0;
}
		

int   Node::GetDof(int dofindex){

	return doflist[dofindex];

}

		
void  Node::CreateVecSets(Vec pv_g,Vec pv_m,Vec pv_n,Vec pv_f,Vec pv_s){

	double gvalue=1.0; //all nodes are in the g set;
	double value;

	int i;

	for(i=0;i<numberofdofs;i++){

		/*g set: */
		VecSetValues(pv_g,1,&doflist[i],&gvalue,INSERT_VALUES);
		
		/*m set: */
		value=(double)mset[i];
		VecSetValues(pv_m,1,&doflist[i],&value,INSERT_VALUES);

		/*n set: */
		value=(double)nset[i];
		VecSetValues(pv_n,1,&doflist[i],&value,INSERT_VALUES);

		/*f set: */
		value=(double)fset[i];
		VecSetValues(pv_f,1,&doflist[i],&value,INSERT_VALUES);

		/*s set: */
		value=(double)sset[i];
		VecSetValues(pv_s,1,&doflist[i],&value,INSERT_VALUES);

	}


}
		
void  Node::GetDofList(int* outdoflist,int* pnumberofdofspernode){

	int i;
	for(i=0;i<numberofdofs;i++){
		outdoflist[i]=doflist[i];
	}
	/*Assign output pointers:*/
	*pnumberofdofspernode=numberofdofs;
}


double Node::GetX(){return x[0];}
double Node::GetY(){return x[1];}
double Node::GetZ(){return x[2];}

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