/*
 * \file DataSet.cpp
 * \brief: Implementation of DataSet class
 */

/*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 "../datastructures/datastructures.h"
#include "../shared/shared.h"

using namespace std;
/*}}}*/

/*Constructors/Destructors*/
DataSet::DataSet(){/*{{{*/

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

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

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

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

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

	DataSet* copy=new DataSet(this->enum_type);

	copy->sorted=this->sorted;
	copy->numsorted=this->numsorted;
	copy->presorted=this->presorted;

	/*Now we need to deep copy the objects: */
	for ( obj=this->objects.begin() ; obj < this->objects.end(); obj++ ){
		/*Call copy on object: */
		object_copy = (*obj)->copy();
		copy->AddObject(object_copy);
	}

	/*Build id_offsets and sorted_ids*/
	int objsize = this->numsorted;
	if(this->sorted && objsize>0 && this->id_offsets){	
		/*Allocate new ids*/
		copy->id_offsets=xNew<int>(objsize);
		xMemCpy<int>(copy->id_offsets,this->id_offsets,objsize);
	}
	else copy->id_offsets=NULL;
	if(this->sorted && objsize>0 && this->sorted_ids){
		/*Allocate new ids*/
		copy->sorted_ids=xNew<int>(objsize);
		xMemCpy<int>(copy->sorted_ids,this->sorted_ids,objsize);
	}
	else copy->sorted_ids=NULL;

	return copy;
}
/*}}}*/
DataSet::~DataSet(){/*{{{*/
	clear();
	xDelete<int>(sorted_ids);
	xDelete<int>(id_offsets);
}
/*}}}*/

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

	_assert_(this);
	objects.push_back(object);

	return 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();
}
/*}}}*/
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;

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

	vector<Object*>::iterator object;

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

	_printf0_("DataSet echo: " << objects.size() << " objects\n");

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

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

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

	vector<Object*>::iterator object;

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

	_printf0_("DataSet echo: " << objects.size() << " objects\n");

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

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

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

	return objects[offset]->ObjectEnum();

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

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

	return objects[offset];

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

	int id_offset;
	int offset;

	_assert_(this);
	if(!sorted || objects.size()>numsorted)_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];
}
/*}}}*/
void  DataSet::Presort(){/*{{{*/

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

		/*Delete existing ids*/
		if(sorted_ids) xDelete<int>(sorted_ids);
		if(id_offsets) 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: */
	numsorted=objects.size();
	sorted=1;
}
/*}}}*/
int   DataSet::Size(void){/*{{{*/
	_assert_(this!=NULL);

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

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