/*
 * \file DataSet.c
 * \brief: implementation of the DataSet class, and derived classes Inputs and Parameters
 */

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

#include <vector>
#include <functional>
#include <algorithm>
#include <iostream>

#include "./DataSet.h"
#include "../objects/objects.h"
#include "../shared/shared.h"
#include "../io/io.h"
#include "../include/include.h"
#include "../EnumDefinitions/EnumDefinitions.h"

using namespace std;
/*}}}*/

/*Constructors/Destructors*/
/*FUNCTION DataSet::DataSet(){{{1*/
DataSet::DataSet(){
	
	sorted=0;
	sorted_ids=NULL;
	id_offsets=NULL;

}
/*}}}*/
/*FUNCTION DataSet::DataSet(int dataset_enum){{{1*/
DataSet::DataSet(int dataset_enum){
	enum_type=dataset_enum;
	
	sorted=0;
	sorted_ids=NULL;
	id_offsets=NULL;

}
/*}}}*/
/*FUNCTION DataSet::Copy{{{1*/
DataSet*   DataSet::Copy(void){

	DataSet* copy=NULL;
	vector<Object*>::iterator object;
	Object* object_copy=NULL;

	copy=new DataSet(enum_type);

	copy->sorted=sorted;
	copy->presorted=presorted;
	if(sorted_ids){
		copy->sorted_ids=(int*)xmalloc(objects.size()*sizeof(int));
		memcpy(copy->sorted_ids,sorted_ids,objects.size()*sizeof(int));
	}
	if(id_offsets){
		copy->id_offsets=(int*)xmalloc(objects.size()*sizeof(int));
		memcpy(copy->id_offsets,id_offsets,objects.size()*sizeof(int));
	}

	/*Now we need to deep copy the objects: */
	for ( object=objects.begin() ; object < objects.end(); object++ ){

		/*Call echo on object: */
		object_copy = (*object)->copy();
		copy->AddObject(object_copy);
	}
	return copy;
}
/*}}}*/
/*FUNCTION DataSet::~DataSet{{{1*/
DataSet::~DataSet(){
	clear();
	xfree((void**)&sorted_ids);
	xfree((void**)&id_offsets);
}
/*}}}*/

/*I/O*/
#ifdef _SERIAL_
/*FUNCTION DataSet::Marshall{{{1*/
char* DataSet::Marshall(){

	vector<Object*>::iterator object;
	int                       object_size;
	int                       marshalled_dataset_size=0;
	char*                     marshalled_dataset=NULL;
	char*                     old_marshalled_dataset=NULL;

	/*First get size of marshalled dataset: */
	object_size=(int)objects.size();

	marshalled_dataset_size=MarshallSize();
	
	/*Allocate marshalled dataset: */
	marshalled_dataset=(char*)xmalloc(marshalled_dataset_size*sizeof(char)); 

	/*Keep track of old_marshalled_dataset: */
	old_marshalled_dataset=marshalled_dataset;

	/*Store internals of dataset first: */
	memcpy(marshalled_dataset,&object_size,sizeof(int)); marshalled_dataset+=sizeof(int);
	memcpy(marshalled_dataset,&sorted,sizeof(int)); marshalled_dataset+=sizeof(int);
	if(sorted){
		if(object_size)memcpy(marshalled_dataset,sorted_ids,object_size*sizeof(int)); marshalled_dataset+=object_size*sizeof(int);
		if(object_size)memcpy(marshalled_dataset,id_offsets,object_size*sizeof(int)); marshalled_dataset+=object_size*sizeof(int);
	}

	for ( object=objects.begin() ; object < objects.end(); object++ ){
		(*object)->Marshall(&marshalled_dataset);
	}

	/* Ok, marshalled_dataset now points to the end of the original marshalled_dataset pointer 
	 * before  we started the loop on objects. Get object to point right again: */
	marshalled_dataset-=marshalled_dataset_size;

	/*We should be back to old_marshalled_dataset: check and abort if that's not the case, 
	 * because this is a nasty error: */
	if (marshalled_dataset!=old_marshalled_dataset){
		_error_("final marshalled dataset \"%s\" is different from initial one!",EnumToStringx(enum_type)); 
		abort();
	}

	/*Return: */
	return marshalled_dataset;
}
/*}}}*/
/*FUNCTION DataSet::MarshallSize{{{1*/
int DataSet::MarshallSize(){

	vector<Object*>::iterator object;
	int                      marshalled_dataset_size=0;


	for ( object=objects.begin() ; object < objects.end(); object++ ){
		marshalled_dataset_size+= (*object)->MarshallSize();
	}

	marshalled_dataset_size+=sizeof(int); //objects size
	marshalled_dataset_size+=sizeof(int); //sorted size
	if(sorted){
		marshalled_dataset_size+=(int)objects.size()*sizeof(int); //sorted ids
		marshalled_dataset_size+=(int)objects.size()*sizeof(int); //id offsets
	}

	return marshalled_dataset_size;
}
/*}}}*/
/*FUNCTION DataSet::Demarshall{{{1*/
DataSet* DataSetDemarshall(char* marshalled_dataset){

	return DataSetDemarshallRaw(&marshalled_dataset);

}
/*}}}*/
/*FUNCTION DataSet::DemarshallRaw{{{1*/
DataSet* DataSetDemarshallRaw(char** pmarshalled_dataset){

	int i;

	DataSet* dataset=NULL;
	int      numobjects=0;
	int      enum_type;
	Object*  object=NULL;
	int      sorted;
	int*     sorted_ids=NULL;
	int*     id_offsets=NULL;
	char*    marshalled_dataset=NULL;

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

	/*initialize dataset: */
	dataset=new DataSet();

	/*Get internals first: */
	memcpy(&numobjects,marshalled_dataset,sizeof(int)); marshalled_dataset+=sizeof(int);
	memcpy(&sorted,marshalled_dataset,sizeof(int)); marshalled_dataset+=sizeof(int);
	if(sorted){
		if(numobjects){
			sorted_ids=(int*)xmalloc(numobjects*sizeof(int));
			id_offsets=(int*)xmalloc(numobjects*sizeof(int));
			memcpy(sorted_ids,marshalled_dataset,numobjects*sizeof(int)); marshalled_dataset+=numobjects*sizeof(int);
			memcpy(id_offsets,marshalled_dataset,numobjects*sizeof(int)); marshalled_dataset+=numobjects*sizeof(int);
		}
		dataset->SetSorting(sorted_ids,id_offsets);
	}

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

		/*get enum type of object: */
		memcpy(&enum_type,marshalled_dataset,sizeof(int)); marshalled_dataset+=sizeof(int);

		switch(enum_type){
			case NodeEnum:{
				Node* node=NULL;
				node=new Node();
				node->Demarshall(&marshalled_dataset);
				dataset->AddObject(node);}
				break;
			case VertexEnum:{
				Vertex* vertex=NULL;
				vertex=new Vertex();
				vertex->Demarshall(&marshalled_dataset);
				dataset->AddObject(vertex);}
				break;
			case DoubleParamEnum:{
				DoubleParam* doubleparam=NULL;
				doubleparam=new DoubleParam();
				doubleparam->Demarshall(&marshalled_dataset);
				dataset->AddObject(doubleparam);}
				break;
			case TriaEnum:{
				Tria* tria=NULL;
				tria=new Tria();
				tria->Demarshall(&marshalled_dataset);
				dataset->AddObject(tria);}
				break;
			case TriaP1InputEnum:{
				TriaP1Input* triavertexinput=NULL;
				triavertexinput=new TriaP1Input();
				triavertexinput->Demarshall(&marshalled_dataset);
				dataset->AddObject(triavertexinput);}
				break;
			#ifdef _HAVE_THREED_
			case PentaP1InputEnum:{
				PentaP1Input* pentavertexinput=NULL;
				pentavertexinput=new PentaP1Input();
				pentavertexinput->Demarshall(&marshalled_dataset);
				dataset->AddObject(pentavertexinput);}
				break;
			#endif
			case TransientInputEnum:{
				TransientInput* transientinput=NULL;
				transientinput=new TransientInput();
				transientinput->Demarshall(&marshalled_dataset);
				dataset->AddObject(transientinput);}
				break;
			#ifdef _HAVE_CONTROL_
			case ControlInputEnum:{
			   ControlInput* controlinputinput=NULL;
				controlinputinput=new ControlInput();
				controlinputinput->Demarshall(&marshalled_dataset);
				dataset->AddObject(controlinputinput);}
				break;
			#endif
			case DatasetInputEnum:{
				DatasetInput* datasetinputinput=NULL;
				datasetinputinput=new DatasetInput();
				datasetinputinput->Demarshall(&marshalled_dataset);
				dataset->AddObject(datasetinputinput);}
				break;
			case TriaP1ElementResultEnum:{
				TriaP1ElementResult* triavertexelementresult=NULL;
				triavertexelementresult=new TriaP1ElementResult();
				triavertexelementresult->Demarshall(&marshalled_dataset);
				dataset->AddObject(triavertexelementresult);}
				break;
			 #ifdef _HAVE_THREED_
			case PentaP1ElementResultEnum:{
				PentaP1ElementResult* pentavertexelementresult=NULL;
				pentavertexelementresult=new PentaP1ElementResult();
				pentavertexelementresult->Demarshall(&marshalled_dataset);
				dataset->AddObject(pentavertexelementresult);}
				break;
			case PentaEnum:{
				Penta* penta=NULL;
				penta=new Penta();
				penta->Demarshall(&marshalled_dataset);
				dataset->AddObject(penta);}
				break;
			#endif
			case MaticeEnum:{
				Matice* matice=NULL;
				matice=new Matice();
				matice->Demarshall(&marshalled_dataset);
				dataset->AddObject(matice);}
				break;
			case MatparEnum:{
				Matpar* matpar=NULL;
				matpar=new Matpar();
				matpar->Demarshall(&marshalled_dataset);
				dataset->AddObject(matpar);}
				break;
			case SpcStaticEnum:{
				SpcStatic* spcstatic=NULL;
				spcstatic=new SpcStatic();
				spcstatic->Demarshall(&marshalled_dataset);
				dataset->AddObject(spcstatic);}
				break;
			case SpcDynamicEnum:{
				SpcDynamic* spcdynamic=NULL;
				spcdynamic=new SpcDynamic();
				spcdynamic->Demarshall(&marshalled_dataset);
				dataset->AddObject(spcdynamic);}
				break;
			case SpcTransientEnum:{
				SpcTransient* spctransient=NULL;
				spctransient=new SpcTransient();
				spctransient->Demarshall(&marshalled_dataset);
				dataset->AddObject(spctransient);}
				break;
			case PengridEnum:{
				Pengrid* pengrid=NULL;
				pengrid=new Pengrid();
				pengrid->Demarshall(&marshalled_dataset);
				dataset->AddObject(pengrid);}
				break;
			case PenpairEnum:{
				Penpair* penpair=NULL;
				penpair=new Penpair();
				penpair->Demarshall(&marshalled_dataset);
				dataset->AddObject(penpair);}
				break;
			case IcefrontEnum:{
				Icefront* icefront=NULL;
				icefront=new Icefront();
				icefront->Demarshall(&marshalled_dataset);
				dataset->AddObject(icefront);}
				break;
			case NumericalfluxEnum:{
				Numericalflux* numericalflux=NULL;
				numericalflux=new Numericalflux();
				numericalflux->Demarshall(&marshalled_dataset);
				dataset->AddObject(numericalflux);}
				break;
			#ifdef _HAVE_RIFTS_
			case RiftfrontEnum:{
				Riftfront* riftfront=NULL;
				riftfront=new Riftfront();
				riftfront->Demarshall(&marshalled_dataset);
				dataset->AddObject(riftfront);}
				break;
			#endif
			case DoubleInputEnum:{
				DoubleInput* doubleinput=NULL;
				doubleinput=new DoubleInput();
				doubleinput->Demarshall(&marshalled_dataset);
				dataset->AddObject(doubleinput);}
				break;
			case IntInputEnum:{
				IntInput* intinput=NULL;
				intinput=new IntInput();
				intinput->Demarshall(&marshalled_dataset);
				dataset->AddObject(intinput);}
				break;
			case BoolInputEnum:{
				BoolInput* boolinput=NULL;
				boolinput=new BoolInput();
				boolinput->Demarshall(&marshalled_dataset);
				dataset->AddObject(boolinput);}
				break;
			case IntParamEnum:{
				IntParam* intparam=NULL;
				intparam=new IntParam();
				intparam->Demarshall(&marshalled_dataset);
				dataset->AddObject(intparam);}
				break;
			case BoolParamEnum:{
				BoolParam* boolparam=NULL;
				boolparam=new BoolParam();
				boolparam->Demarshall(&marshalled_dataset);
				dataset->AddObject(boolparam);}
				break;
			case StringParamEnum:{
				StringParam* stringparam=NULL;
				stringparam=new StringParam();
				stringparam->Demarshall(&marshalled_dataset);
				dataset->AddObject(stringparam);}
				break;
			case DoubleVecExternalResultEnum:{
				DoubleVecExternalResult* doublevecexternalresult=NULL;
				doublevecexternalresult=new DoubleVecExternalResult();
				doublevecexternalresult->Demarshall(&marshalled_dataset);
				dataset->AddObject(doublevecexternalresult);}
				break;
			case DoubleExternalResultEnum:{
				DoubleExternalResult* doubleexternalresult=NULL;
				doubleexternalresult=new DoubleExternalResult();
				doubleexternalresult->Demarshall(&marshalled_dataset);
				dataset->AddObject(doubleexternalresult);}
				break;
			#ifdef _HAVE_GROUNDINGLINE_
			case BoolElementResultEnum:{
				BoolElementResult* boolelementresult=NULL;
				boolelementresult=new BoolElementResult();
				boolelementresult->Demarshall(&marshalled_dataset);
				dataset->AddObject(boolelementresult);}
				break;
			#endif
			default:
				_error_("could not recognize enum type: %s",EnumToStringx(enum_type));
		}
	}

	/*Assign output pointers:*/
	*pmarshalled_dataset=marshalled_dataset;
	
	return dataset;
}
/*}}}*/
#endif

/*Specific methods*/
/*FUNCTION DataSet::AddObject{{{1*/
int  DataSet::AddObject(Object* object){

	objects.push_back(object);

	return 1;
}
/*}}}*/
/*FUNCTION DataSet::clear{{{1*/
void  DataSet::clear(){

/*  use reverse_iterator for efficiency in matlab memory manager
	(keeping old code in case it needs to revert back)  */

//	vector<Object*>::iterator object;
	vector<Object*>::reverse_iterator object;

//	for ( object=objects.begin() ; object < objects.end(); object++ ){
//		delete (*object);
//	}
	for ( object=objects.rbegin() ; object < objects.rend(); object++ ){
		delete (*object);
	}
	objects.clear();
}
/*}}}*/
/*FUNCTION DataSet::DeleteObject{{{1*/
int  DataSet::DeleteObject(Object* object){

	vector<Object*>::iterator iterator;

	if(object){
		iterator = find(objects.begin(), objects.end(),object);
		delete *iterator;
		objects.erase(iterator);
	}

	return 1;

}
/*}}}*/
/*FUNCTION DataSet::DeepEcho{{{1*/
void DataSet::DeepEcho(){


	vector<Object*>::iterator object;

	if(this==NULL)_error_(" trying to echo a NULL dataset");

	_printf_(true,"DataSet echo: %i objects\n",objects.size());

	for ( object=objects.begin() ; object < objects.end(); object++ ){

		/*Call deep echo on object: */
		(*object)->DeepEcho();

	}
}
/*}}}*/
/*FUNCTION DataSet::Echo{{{1*/
void DataSet::Echo(){

	vector<Object*>::iterator object;

	if(this==NULL)_error_(" trying to echo a NULL dataset");

	_printf_(true,"DataSet echo: %i objects\n",objects.size());

	for ( object=objects.begin() ; object < objects.end(); object++ ){

		/*Call echo on object: */
		(*object)->Echo();

	}
	return;
}
/*}}}*/
/*FUNCTION DataSet::GetEnum(){{{1*/
int  DataSet::GetEnum(){
	return enum_type;
}
/*}}}*/
/*FUNCTION DataSet::GetEnum(int offset){{{1*/
int   DataSet::GetEnum(int offset){

	return objects[offset]->ObjectEnum();

}
/*}}}*/
/*FUNCTION DataSet::GetObjectByOffset{{{1*/
Object* DataSet::GetObjectByOffset(int offset){

	/*Check index in debugging mode*/
	_assert_(this!=NULL);
	_assert_(offset<this->Size());

	return objects[offset];

}
/*}}}*/
/*FUNCTION DataSet::GetObjectById{{{1*/
Object* DataSet::GetObjectById(int* poffset,int eid){

	int id_offset;
	int offset;
	int i;

	_assert_(this);
	if(!sorted)_error_(" trying to binary search on a non-sorted dataset!");

	/*Carry out a binary search on the sorted_ids: */
	if(!binary_search(&id_offset,eid, sorted_ids,objects.size())){
		_error_("could not find object with id %i in DataSet %s",eid,EnumToStringx(enum_type));
	}

	/*Convert  the id offset into sorted offset: */
	offset=id_offsets[id_offset];

	/*Assign output pointers if requested:*/
	if (poffset)*poffset=offset;

	/*Return object at offset position in objects :*/
	return objects[offset];
}
/*}}}*/
/*FUNCTION DataSet::Presort{{{1*/
void DataSet::Presort(){

	/*vector of objects is already sorted, just allocate the sorted ids and their
	 * offsets:*/
	int i;

	if(objects.size()){

		/*Delete existing ids*/
		xfree((void**)&sorted_ids);
		xfree((void**)&id_offsets);

		/*Allocate new ids*/
		sorted_ids=(int*)xmalloc(objects.size()*sizeof(int));
		id_offsets=(int*)xmalloc(objects.size()*sizeof(int));

		/*Build id_offsets and sorted_ids*/
		for(i=0;i<objects.size();i++){
			id_offsets[i]=i;
			sorted_ids[i]=objects[i]->Id();
		}
	}

	/*set sorted flag: */
	sorted=1;
}
/*}}}*/
/*FUNCTION DataSet::SetSorting{{{1*/
void DataSet::SetSorting(int* in_sorted_ids,int* in_id_offsets){

	sorted=1;
	sorted_ids=in_sorted_ids;
	id_offsets=in_id_offsets;
}
/*}}}*/
/*FUNCTION DataSet::Size{{{1*/
int  DataSet::Size(void){
	_assert_(this!=NULL);

	return objects.size();
}
/*}}}*/
/*FUNCTION DataSet::Sort{{{1*/
void DataSet::Sort(){

	/*Only sort if we are not already sorted: */
	if(!sorted){
		_error_(" not implemented yet!");
	}
}
/*}}}*/
