#include "./SmoothedSurfaceSlopeXAnalysis.h"
#include "../toolkits/toolkits.h"
#include "../classes/classes.h"
#include "../shared/shared.h"
#include "../modules/modules.h"

/*Model processing*/
int  SmoothedSurfaceSlopeXAnalysis::DofsPerNode(int** doflist,int domaintype,int approximation){/*{{{*/
	return 1;
}/*}}}*/
void SmoothedSurfaceSlopeXAnalysis::UpdateParameters(Parameters* parameters,IoModel* iomodel,int solution_enum,int analysis_enum){/*{{{*/
}/*}}}*/
void SmoothedSurfaceSlopeXAnalysis::UpdateElements(Elements* elements,IoModel* iomodel,int analysis_counter,int analysis_type){/*{{{*/

	/*Update elements: */
	int counter=0;
	for(int i=0;i<iomodel->numberofelements;i++){
		if(iomodel->my_elements[i]){
			Element* element=(Element*)elements->GetObjectByOffset(counter);
			element->Update(i,iomodel,analysis_counter,analysis_type,P1Enum);
			counter++;
		}
	}

	iomodel->FetchDataToInput(elements,SurfaceEnum);
	iomodel->FetchDataToInput(elements,BaseEnum);
	iomodel->FetchDataToInput(elements,MaskIceLevelsetEnum);
	if(iomodel->domaintype!=Domain2DhorizontalEnum){
		iomodel->FetchDataToInput(elements,MeshVertexonbaseEnum);
		iomodel->FetchDataToInput(elements,MeshVertexonsurfaceEnum);
	}
}/*}}}*/
void SmoothedSurfaceSlopeXAnalysis::CreateNodes(Nodes* nodes,IoModel* iomodel){/*{{{*/
	if(iomodel->domaintype==Domain3DEnum) iomodel->FetchData(1,MeshVertexonbaseEnum);
	::CreateNodes(nodes,iomodel,SmoothedSurfaceSlopeXAnalysisEnum,P1Enum);
	if(iomodel->domaintype==Domain3DEnum) iomodel->DeleteData(1,MeshVertexonbaseEnum);
}/*}}}*/
void SmoothedSurfaceSlopeXAnalysis::CreateConstraints(Constraints* constraints,IoModel* iomodel){/*{{{*/
}/*}}}*/
void SmoothedSurfaceSlopeXAnalysis::CreateLoads(Loads* loads, IoModel* iomodel){/*{{{*/
}/*}}}*/

/*Finite Element Analysis*/
void           SmoothedSurfaceSlopeXAnalysis::Core(FemModel* femmodel){/*{{{*/
	_error_("not implemented");
}/*}}}*/
ElementVector* SmoothedSurfaceSlopeXAnalysis::CreateDVector(Element* element){/*{{{*/
	/*Default, return NULL*/
	return NULL;
}/*}}}*/
ElementMatrix* SmoothedSurfaceSlopeXAnalysis::CreateJacobianMatrix(Element* element){/*{{{*/
_error_("Not implemented");
}/*}}}*/
ElementMatrix* SmoothedSurfaceSlopeXAnalysis::CreateKMatrix(Element* element){/*{{{*/

	/* Intermediaries */
	IssmDouble  Jdet,thickness,l=8.;
	IssmDouble *xyz_list = NULL;

	/*Fetch number of nodes and dof for this finite element*/
	int numnodes = element->GetNumberOfNodes();

	/*Initialize Element matrix and vectors*/
	ElementMatrix* Ke     = element->NewElementMatrix();
	IssmDouble*    dbasis = xNew<IssmDouble>(2*numnodes);
	IssmDouble*    basis  = xNew<IssmDouble>(numnodes);

	/*Retrieve all inputs and parameters*/
	element->GetVerticesCoordinates(&xyz_list);
	Input* thickness_input = element->GetInput(ThicknessEnum); _assert_(thickness_input);

	/* Start looping on the number of gaussian points: */
	Gauss* gauss=element->NewGauss(2);
	for(int ig=gauss->begin();ig<gauss->end();ig++){
		gauss->GaussPoint(ig);

		element->JacobianDeterminant(&Jdet,xyz_list,gauss);
		thickness_input->GetInputValue(&thickness,gauss);
		if(thickness<50.) thickness=50.;

		element->NodalFunctions(basis,gauss);
		element->NodalFunctionsDerivatives(dbasis,xyz_list,gauss);

		for(int i=0;i<numnodes;i++){
			for(int j=0;j<numnodes;j++){
				Ke->values[i*numnodes+j] += gauss->weight*Jdet*(
							basis[i]*basis[j]
							+(l*thickness)*(l*thickness)*(dbasis[0*numnodes+i]*dbasis[0*numnodes+j] + dbasis[1*numnodes+i]*dbasis[1*numnodes+j])
							);
			}
		}
	}

	/*Clean up and return*/
	delete gauss;
	xDelete<IssmDouble>(dbasis);
	xDelete<IssmDouble>(basis);
	xDelete<IssmDouble>(xyz_list);
	return Ke;
}/*}}}*/
ElementVector* SmoothedSurfaceSlopeXAnalysis::CreatePVector(Element* element){/*{{{*/

	/*Intermediaries*/
	int      domaintype;
	Element* basalelement;

	/*Get basal element*/
	element->FindParam(&domaintype,DomainTypeEnum);
	switch(domaintype){
		case Domain2DhorizontalEnum:
			basalelement = element;
			break;
		case Domain3DEnum:
			if(!element->IsOnBase()) return NULL;
			basalelement = element->SpawnBasalElement();
			break;
		default: _error_("mesh "<<EnumToStringx(domaintype)<<" not supported yet");
	}

	/*Intermediaries */
	int         input_enum;
	IssmDouble  Jdet,thickness,slope[2];
	IssmDouble  taud_x,norms,normv,vx,vy;
	IssmDouble *xyz_list  = NULL;

	/*Fetch number of nodes and dof for this finite element*/
	int numnodes = basalelement->GetNumberOfNodes();

	/*Initialize Element vector*/
	ElementVector* pe    = basalelement->NewElementVector();
	IssmDouble*    basis = xNew<IssmDouble>(numnodes);

	/*Retrieve all inputs and parameters*/
	basalelement->GetVerticesCoordinates(&xyz_list);
	IssmDouble rho_ice   = element->GetMaterialParameter(MaterialsRhoIceEnum);
	IssmDouble gravity   = element->GetMaterialParameter(ConstantsGEnum);
	Input* H_input       = basalelement->GetInput(ThicknessEnum); _assert_(H_input);
	Input* surface_input = basalelement->GetInput(SurfaceEnum);   _assert_(surface_input);
	Input* vx_input      = basalelement->GetInput(VxEnum);
	Input* vy_input      = basalelement->GetInput(VyEnum);

	/* Start  looping on the number of gaussian points: */
	Gauss* gauss=basalelement->NewGauss(2);
	for(int ig=gauss->begin();ig<gauss->end();ig++){
		gauss->GaussPoint(ig);

		basalelement->JacobianDeterminant(&Jdet,xyz_list,gauss);
		basalelement->NodalFunctions(basis,gauss);

		H_input->GetInputValue(&thickness,gauss);
		surface_input->GetInputDerivativeValue(&slope[0],xyz_list,gauss);
		if(vx_input && vy_input){
			vx_input->GetInputValue(&vx,gauss);
			vy_input->GetInputValue(&vy,gauss);
			norms = sqrt(slope[0]*slope[0]+slope[1]*slope[1]+1.e-10);
			normv = sqrt(vx*vx + vy*vy);
			if(normv>15./(365.*24.*3600.)) slope[0] = -vx/normv*norms;
		}
		taud_x = rho_ice*gravity*thickness*slope[0];

		for(int i=0;i<numnodes;i++) pe->values[i]+=Jdet*gauss->weight*taud_x*basis[i];
	}

	/*Clean up and return*/
	xDelete<IssmDouble>(xyz_list);
	xDelete<IssmDouble>(basis);
	delete gauss;
	if(domaintype!=Domain2DhorizontalEnum){basalelement->DeleteMaterials(); delete basalelement;};
	return pe;
}/*}}}*/
void SmoothedSurfaceSlopeXAnalysis::GetSolutionFromInputs(Vector<IssmDouble>* solution,Element* element){/*{{{*/
	   _error_("not implemented yet");
}/*}}}*/
void SmoothedSurfaceSlopeXAnalysis::GradientJ(Vector<IssmDouble>* gradient,Element* element,int control_type,int control_index){/*{{{*/
	_error_("Not implemented yet");
}/*}}}*/
void SmoothedSurfaceSlopeXAnalysis::InputUpdateFromSolution(IssmDouble* solution,Element* element){/*{{{*/
	element->InputUpdateFromSolutionOneDof(solution,SurfaceSlopeXEnum);
}/*}}}*/
void SmoothedSurfaceSlopeXAnalysis::UpdateConstraints(FemModel* femmodel){/*{{{*/
	/*Default, do nothing*/
	return;
}/*}}}*/
