/*!\file IndependentObject.c
 * \brief: implementation of the IndependentObject 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 "IoModel.h"

/*IndependentObject constructors and destructor*/
IndependentObject::IndependentObject(){/*{{{*/
	this->name=NoneEnum;
	this->type=0;
	this->numberofindependents=0;
}
/*}}}*/
IndependentObject::IndependentObject(int in_name, int in_type){/*{{{*/

	this->numberofindependents=0;
	this->name=in_name;
	this->type=in_type;
	if(in_type!=0 && in_type!=1)_error_("cannot create an IndependentObject of type " << in_type);

}
/*}}}*/
IndependentObject::~IndependentObject(){ //destructor/*{{{*/
}
/*}}}*/

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

	_printf_("IndependentObject:\n");
	_printf_("   name: " << EnumToStringx(this->name) << "\n");
	if(this->type==0)
		_printf_("   type: scalar\n");
	else if(this->type==1)
		_printf_("   type: vertex\n");
	else
		_error_(" unknown type: " << this->type);
	_printf_("   numberofindependents: " << this->numberofindependents << "\n");
}
/*}}}*/
void IndependentObject::DeepEcho(void){/*{{{*/
	this->Echo();
}
/*}}}*/
int    IndependentObject::Id(void){ return -1; }/*{{{*/
/*}}}*/
int IndependentObject::ObjectEnum(void){/*{{{*/

	return IndependentObjectEnum;

}
/*}}}*/
Object* IndependentObject::copy(void) { /*{{{*/

	IndependentObject* object=new IndependentObject();
	object->name=this->name;
	object->type=this->type;
	object->numberofindependents=this->numberofindependents;

	return  object;
} /*}}}*/

/*IndependentObject methods: */
void IndependentObject::FetchIndependent(IoModel* iomodel){/*{{{*/

	int my_rank;
	FILE* fid=NULL;

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

	#ifdef _HAVE_ADOLC_ //cannot come here unless you are running AD mode, from DeclaredIndependents:

	/*Branch according to the type of variable: */
	if(type==0){ /*scalar: {{{*/

		/*output: */
		IssmPDouble  pscalar;
		IssmDouble   scalar; //same as pscalar, except it's an ADOLC independent variable
		IssmDouble*  scalar_slot=NULL;
		int      code;

		/*Set file pointer to beginning of the data: */
		fid=iomodel->SetFilePointerToData(&code,NULL,name);

		if(code!=3)_error_("expecting a IssmDouble for enum " << EnumToStringx(name));

		/*We have to read a scalar from disk. First read the dimensions of the scalar, then the scalar: */
		if(my_rank==0){
			if(fread(&pscalar,sizeof(IssmPDouble),1,fid)!=1)_error_("could not read scalar ");

			/*Now, before we even broadcast this to other nodes, declare the scalar  as an independent variable!: */
			scalar<<=pscalar;
		}

		ISSM_MPI_Bcast(&scalar,1,ISSM_MPI_DOUBLE,0,IssmComm::GetComm()); 

		/*Ok, we are almost done. scalar is now an independent variable. We don't want this variable to be fetched again in the 
		 *future, which would effectively write over the independency in the ADOLC tape! So we are going to keep track of this 
		 independent variable inthe iomodel->data[name] data slot. Because this data slot holds double*, we allocate a sizeof(double)
		 space for it: */
		scalar_slot=xNew<IssmDouble>(1); *scalar_slot=scalar;

		iomodel->data[name]=scalar_slot;
		iomodel->independents[name]=true;

		//finally, record the number of independents:
		this->numberofindependents=1;

	} /*}}}*/
	else if(type==1){ /* vector: {{{*/

		FILE* fid=NULL;
		int M,N;
		IssmPDouble* buffer=NULL; //a buffer to read the data from disk
		IssmDouble* matrix=NULL; //our independent variable
		int code=0;

		/*Set file pointer to beginning of the data: */
		fid=iomodel->SetFilePointerToData(&code,NULL,name);
		if((code!=5) && (code!=6) && (code!=7))_error_("expecting a IssmDouble, integer or boolean matrix for enum " << EnumToStringx(name));

		/*We have to read a matrix from disk. First read the dimensions of the matrix, then the whole matrix: */
		/*numberofelements: */
		if(my_rank==0){  
			if(fread(&M,sizeof(int),1,fid)!=1) _error_("could not read number of rows for matrix ");
		}
		ISSM_MPI_Bcast(&M,1,ISSM_MPI_INT,0,IssmComm::GetComm()); 

		if(my_rank==0){  
			if(fread(&N,sizeof(int),1,fid)!=1) _error_("could not read number of columns for matrix ");
		}
		ISSM_MPI_Bcast(&N,1,ISSM_MPI_INT,0,IssmComm::GetComm()); 

		/*Now allocate matrix: */
		if(M*N){
			buffer=xNew<IssmPDouble>(M*N);
			matrix=xNew<IssmDouble>(M*N);

			/*Read matrix on node 0, then broadcast: */
			if(my_rank==0){  
				if(fread(buffer,M*N*sizeof(IssmPDouble),1,fid)!=1) _error_("could not read matrix ");

				/*Now, before we even broadcast this to other nodes, declare the whole matrix as a independent variable!: */
				for (int i=0;i<M*N;++i) matrix[i]<<=buffer[i];  /*we use the <<= ADOLC overloaded operator to declare the independency*/
			}
			ISSM_MPI_Bcast(matrix,M*N,ISSM_MPI_DOUBLE,0,IssmComm::GetComm()); 

			xDelete<IssmPDouble>(buffer);
		}
		else _error_("cannot declare the independent variable " << EnumToStringx(name) <<  "if it's empty!");

		/*Ok, we are almost done. Matrix is now a independent matrix. We don't want this matrix to be fetched again in the 
		 *future, which would effectively write over the independency in the ADOLC tape! So we are going to keep track of this 
		 independent matrix inthe iomodel->data[name] data slot: */
		iomodel->data[name]=matrix;
		iomodel->independents[name]=true;

		//Finally, record the number of independents created: 
		this->numberofindependents=M*N;

	}/*}}}*/
	else _error_("should not have a type of " << type);

	#endif
}
/*}}}*/
int  IndependentObject::NumIndependents(void){/*{{{*/

	return this->numberofindependents;;
}
/*}}}*/
void IndependentObject::FillIndependents(IssmDouble** data, IssmDouble* xp){/*{{{*/

	int i;

	/*Branch according to the type of variable: */
	if(type==0){ /*scalar:*/
		xp[0]=*(data[name]);
	}
	else if(type==1){ /* vector:*/
		IssmDouble* values=data[name];
		for(i=0;i<this->numberofindependents;i++){
			xp[i]=values[i];
		}
	}
	else _error_("should not have a type of " << type);
}
/*}}}*/
