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

/*Include files: {{{*/
#ifdef HAVE_CONFIG_H
	#include <config.h>
#else
#error "Cannot compile with HAVE_CONFIG_H symbol! run configure first!"
#endif

#include <string.h>
#include "classes.h"
#include "shared/shared.h"
/*}}}*/

/*Vertex constructors and destructor:*/
Vertex::Vertex(){/*{{{*/
	return;
}
/*}}}*/
Vertex::Vertex(int vertex_id, int vertex_sid,int i, IoModel* iomodel){/*{{{*/

	this->id           = vertex_id;
	this->sid          = vertex_sid;
	this->pid          = UNDEF;

	_assert_(iomodel->Data(MeshXEnum) && iomodel->Data(MeshYEnum) && iomodel->Data(MeshZEnum));
	this->x            = iomodel->Data(MeshXEnum)[i];
	this->y            = iomodel->Data(MeshYEnum)[i];
	this->z            = iomodel->Data(MeshZEnum)[i];
	this->domaintype     = iomodel->domaintype;

	_assert_(iomodel->Data(BaseEnum) && iomodel->Data(ThicknessEnum) && iomodel->numbernodetoelementconnectivity);
	switch(iomodel->domaintype){
		case Domain3DEnum:
		case Domain2DhorizontalEnum:
			this->sigma = (iomodel->Data(MeshZEnum)[i]-iomodel->Data(BaseEnum)[i])/(iomodel->Data(ThicknessEnum)[i]);
			break;
		case Domain2DverticalEnum:
			this->sigma = (iomodel->Data(MeshYEnum)[i]-iomodel->Data(BaseEnum)[i])/(iomodel->Data(ThicknessEnum)[i]);
			break;
	}

	this->connectivity = iomodel->numbernodetoelementconnectivity[i];

}
/*}}}*/
Vertex::~Vertex(){/*{{{*/
	return;
}
/*}}}*/

/*Object virtual functions definitions:*/
void Vertex::Echo(void){/*{{{*/

	_printf_("Vertex:\n");
	_printf_("   id: " << id << "\n");
	_printf_("   sid: " << sid << "\n");
	_printf_("   pid: " << pid << "\n");
	_printf_("   x: " << x << "\n");
	_printf_("   y: " << y << "\n");
	_printf_("   z: " << z << "\n");
	_printf_("   sigma: " << sigma << "\n");
	_printf_("   connectivity: " << connectivity << "\n");
	_printf_("   clone: " << clone << "\n");

	return;
}
/*}}}*/
void Vertex::DeepEcho(void){/*{{{*/
	this->Echo();
}
/*}}}*/
int Vertex::Id(void){ return id; }/*{{{*/
/*}}}*/
int Vertex::ObjectEnum(void){/*{{{*/

	return VertexEnum;

}
/*}}}*/
Object* Vertex::copy() {/*{{{*/

	return new Vertex(*this); 

}
/*}}}*/

/*Vertex management: */
int        Vertex::Connectivity(void){return connectivity;}/*{{{*/
/*}}}*/
IssmDouble Vertex::GetX(){/*{{{*/
	return this->x;
}
/*}}}*/
IssmDouble Vertex::GetY(){/*{{{*/
	return this->y;
}
/*}}}*/
IssmDouble Vertex::GetZ(){/*{{{*/
	return this->z;
}
/*}}}*/
int        Vertex::Sid(void){ return sid; }/*{{{*/
/*}}}*/
int        Vertex::Pid(void){ return pid; }/*{{{*/
/*}}}*/
void       Vertex::UpdatePosition(Vector<IssmDouble>* vx,Vector<IssmDouble>* vy,Vector<IssmDouble>* vz,Parameters* parameters,IssmDouble* surface,IssmDouble* bed){/*{{{*/

	IssmDouble oldy,newy,vely;
	IssmDouble oldz,newz,velz;
	IssmDouble dt;

	/*Get time stepping*/
	parameters->FindParam(&dt,TimesteppingTimeStepEnum);

	/*sigma remains constant. z=bed+sigma*thickness*/
	switch(this->domaintype){
		case Domain2DhorizontalEnum:
			/*Nothing*/
			return;
		case Domain2DverticalEnum:
			oldy = this->y;
			newy = bed[this->pid]+sigma*(surface[this->pid] - bed[this->pid]);
			vely = (newy-oldy)/dt;
			this->y = newy;
			vy->SetValue(this->pid,vely,INS_VAL);
			return;
		case Domain3DEnum:
			oldz = this->z;
			newz = bed[this->pid]+sigma*(surface[this->pid] - bed[this->pid]);
			velz = (newz-oldz)/dt;
			this->z = newz;
			vz->SetValue(this->pid,velz,INS_VAL);
			return;
		default:
			_error_("not implemented");
	}
}
/*}}}*/
void       Vertex::DistributePids(int* ppidcount){/*{{{*/

	/*retrieve current pid*/
	int pidcount=*ppidcount;

	/*This vertex is a clone! Don't distribute pids, it will get them from another cpu!*/
	if(this->clone) return;

	/*This vertex should distribute its pid*/
	this->pid=pidcount;
	pidcount++;

	/*Assign output pointers: */
	*ppidcount=pidcount;
}
/*}}}*/
void       Vertex::OffsetPids(int pidcount){/*{{{*/

	/*This vertex is a clone, don't offset the pids*/
	if(this->clone) return;

	/*This vertex should offset his pid, go ahead: */
	this->pid+=pidcount;
}
/*}}}*/
void       Vertex::ShowTruePids(int* truepids){/*{{{*/

	/*Are we a clone? : */
	if(this->clone)return;

	/*Ok, we are not a clone, just plug our pid into truepids: */
	truepids[this->sid]=this->pid;
}
/*}}}*/
void       Vertex::UpdateClonePids(int* alltruepids){/*{{{*/

	/*If we are not a clone, don't update, we already have pids: */
	if(!this->clone)return;

	/*Ok, we are a clone node, but we did not create the pid for this vertex 
	 * Therefore, our pid is garbage right now. Go pick it up in the alltruepids: */
	this->pid=alltruepids[this->sid];
}
/*}}}*/
void       Vertex::SetClone(int* minranks){/*{{{*/

	int my_rank;

	/*recover my_rank:*/
	my_rank=IssmComm::GetRank();

	if (minranks[this->sid]==my_rank){
		this->clone=false;
	}
	else{
		/*!there is a cpu with lower rank that has the same vertex, 
		therefore, I am a clone*/
		this->clone=true;
	}

}
/*}}}*/
void       Vertex::ToXYZ(Matrix<IssmDouble>* matrix){/*{{{*/

	IssmDouble xyz[3];
	int        indices[3];

	if (this->clone==true) return;

	xyz[0]=x;
	xyz[1]=y; 
	xyz[2]=z;
	indices[0]=0;
	indices[1]=1; 
	indices[2]=2;

	matrix->SetValues(1,&sid,3,&indices[0],&xyz[0],INS_VAL);
}
/*}}}*/
void       Vertex::VertexCoordinates(Vector<IssmDouble>* vx,Vector<IssmDouble>* vy,Vector<IssmDouble>* vz){/*{{{*/

	if (this->clone==true) return;

	vx->SetValue(this->sid,this->x,INS_VAL);
	vy->SetValue(this->sid,this->y,INS_VAL);
	vz->SetValue(this->sid,this->z,INS_VAL);

	return;
}
/*}}}*/

/*Methods relating to Vertex, but not internal methods: */
void GetVerticesCoordinates(IssmDouble* xyz,Vertex** vertices, int numvertices){ /*{{{*/

	_assert_(vertices);
	_assert_(xyz);

	for(int i=0;i<numvertices;i++) {
		xyz[i*3+0]=vertices[i]->GetX();
		xyz[i*3+1]=vertices[i]->GetY();
		xyz[i*3+2]=vertices[i]->GetZ();
	}
}/*}}}*/
