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

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

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

#include "./DataSet.h"
#include "../classes/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(){{{*/
DataSet::DataSet(){

	sorted=0;
	sorted_ids=NULL;
	id_offsets=NULL;

}
/*}}}*/
/*FUNCTION DataSet::DataSet(int dataset_enum){{{*/
DataSet::DataSet(int dataset_enum){
	enum_type=dataset_enum;

	sorted=0;
	sorted_ids=NULL;
	id_offsets=NULL;

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

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

	DataSet* copy=new DataSet(enum_type);

	copy->sorted=sorted;
	copy->presorted=presorted;
	if(sorted_ids){
		copy->sorted_ids=xNew<int>(objects.size());
		xMemCpy<int>(copy->sorted_ids,sorted_ids,objects.size());
	}
	if(id_offsets){
		copy->id_offsets=xNew<int>(objects.size());
		xMemCpy<int>(copy->id_offsets,id_offsets,objects.size());
	}

	/*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{{{*/
DataSet::~DataSet(){
	clear();
	xDelete<int>(sorted_ids);
	xDelete<int>(id_offsets);
}
/*}}}*/

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

	objects.push_back(object);

	return 1;
}
/*}}}*/
/*FUNCTION DataSet::clear{{{*/
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{{{*/
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{{{*/
void DataSet::DeepEcho(){

	vector<Object*>::iterator object;

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

	_pprintLine_("DataSet echo: " << objects.size() << " objects");

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

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

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

	vector<Object*>::iterator object;

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

	_pprintLine_("DataSet echo: " << objects.size() << " objects");

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

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

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

	return objects[offset]->ObjectEnum();

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

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

	return objects[offset];

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

	int id_offset;
	int offset;

	_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 " << eid << " in DataSet " << 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{{{*/
void DataSet::Presort(){

	/*vector of objects is already sorted, just allocate the sorted ids and their
	 * offsets:*/
	if(objects.size()){

		/*Delete existing ids*/
		xDelete<int>(sorted_ids);
		xDelete<int>(id_offsets);

		/*Allocate new ids*/
		sorted_ids=xNew<int>(objects.size());
		id_offsets=xNew<int>(objects.size());

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

	/*set sorted flag: */
	sorted=1;
}
/*}}}*/
/*FUNCTION DataSet::SetSorting{{{*/
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{{{*/
int  DataSet::Size(void){
	_assert_(this!=NULL);

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

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