/*!\file Hook.cpp
 * \brief: implementation of the Hook object: see Hook.h for more explanations.
 */

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

#include <stdio.h>
#include <string.h>
#include "./classes.h"
#include "../Container/Container.h"
#include "../EnumDefinitions/EnumDefinitions.h"
#include "../shared/shared.h"
#include "../include/include.h"

/*Constructor/Destructors*/
/*FUNCTION Hook::Hook(){{{*/
Hook::Hook(){
	this->num     = 0;
	this->objects = NULL;
	this->ids     = NULL;
	this->offsets = NULL;
}
/*}}}*/
/*FUNCTION Hook::Hook(int* ids, int num){{{*/
Hook::Hook(int* in_ids, int in_num){

	/*Get number of objects to hook*/
	this->num=in_num;

	/*Get out if num=0*/
	if (num==0){
		/*Empty hook*/
		this->ids     = NULL;
		this->objects = NULL;
		this->offsets = NULL;
	}
	else{
		/*Allocate: */
		this->objects=xNew<Object*>(this->num);
		this->ids=xNew<int>(this->num);
		this->offsets=xNew<int>(this->num);

		/*Copy ids: */
		for(int i=0;i<this->num;i++){
			this->ids[i]     = in_ids[i];
			this->objects[i] = NULL;
			this->offsets[i] = 0;
		}
	}
}
/*}}}*/
/*FUNCTION Hook::~Hook(){{{*/
Hook::~Hook(){
	xDelete<Object*>(this->objects);
	xDelete<int>(this->ids);
	xDelete<int>(this->offsets);
}
/*}}}*/

/*Some of the Object functionality: */
/*FUNCTION Hook::Echo{{{*/
void Hook::Echo(void){

	int i;
	if (num){
		_printLine_("   Hook: ");
		_printLine_("      num=" << this->num);
		_printString_("      ids: ");
		for (i=0;i<this->num;i++) _printString_(this->ids[i] << " ");
		_printLine_("");
		_printString_("      offsets: ");
		for (i=0;i<this->num;i++) _printString_(this->offsets[i] << " ");
		_printLine_("");
	}
	else{
		_printLine_("   Hook: num=0 ");
	}
}
/*}}}*/
/*FUNCTION Hook::DeepEcho{{{*/
void Hook::DeepEcho(void){

	int i;
	if (num){
		_printLine_("   Hook: ");
		_printLine_("      num=" << this->num);
		_printString_("      ids: ");
		for (i=0;i<this->num;i++) _printString_(this->ids[i] << " ");
		_printLine_("");
		_printString_("      offsets: ");
		for (i=0;i<this->num;i++) _printString_(this->offsets[i] << " ");
		_printLine_("");
		if (!objects) _printLine_("      warning: object not hooked yet");
		else{
			_printString_("      objects:\n   ");
			for (i=0;i<this->num;i++){
				_printLine_("         object " << i);
				if(objects[i]) objects[i]->DeepEcho();
				else           _printLine_("            no object hooked yet (not configured)");
			}
		}
	}
	else{
		_printLine_("   Hook: num=0 ");
	}
}
/*}}}*/
/*FUNCTION Hook::copy {{{*/
Object* Hook::copy(void){

	/*output: */
	Hook* output=NULL;

	/*initalize output: */
	output=new Hook();

	/*copy in the fields: */
	output->num=this->num;
	if(output->num){
		output->objects = xNew<Object*>(output->num);
		output->ids     = xNew<int>(output->num);
		output->offsets = xNew<int>(output->num);
	}

	for(int i=0;i<output->num;i++){
		output->objects[i] = this->objects[i];
		output->offsets[i] = this->offsets[i];
		output->ids[i]     = this->ids[i];
	}

	return (Object*)output;
}
/*}}}*/

/*Hook management: */
/*FUNCTION Hook::configure{{{*/
void Hook::configure(DataSet* dataset){

	/*intermediary: */
	Object* object=NULL;
	int i;

	/*Checks if debugging mode*/
	_assert_(this->num==0 || this->ids!=NULL);

	for(i=0;i<this->num;i++){

		/*is this object id -1? If so, drop this search, it was not requested: */
		if (this->ids[i]==-1) continue;

		/*Check whether existing this->objects are correct: */
		if(this->objects[i]){
			if(this->objects[i]->Id()==this->ids[i]) continue; //this node is good.
			else this->objects[i]=NULL; //this node was incorrect, reset it.
		}

		/*May be the object this->offsets into this->objects are valid?: */
		if(this->offsets[i]!=UNDEF){
			/* Look at the this->offsets[i]'th node in the nodes dataset. If it has the correct id, 
			 * we are good: */
			object=(Object*)dataset->GetObjectByOffset(this->offsets[i]);
			if (object->Id()==this->ids[i]){
				this->objects[i]=object;
				continue;
			}
			else this->offsets[i]=UNDEF; //object offset was wrong, reset it.
		}

		/*Now, for this->objects that did not get resolved, and for which we have no offset, chase them in the dataset, by id: */
		if(this->objects[i]==NULL){
			this->objects[i]=dynamic_cast<Object*>(dataset->GetObjectById(this->offsets+i,this->ids[i])); //remember the offset for later on.
			/*check the id is correct!: */
			if (this->objects[i]->Id()!=this->ids[i]) _error_("wrong id: " << this->objects[i]->Id() << " vs " << this->ids[i] << "  in resolved pointer!");
		}
	}
}
/*}}}*/
/*FUNCTION Hook::delivers{{{*/
Object* Hook::delivers(void){

	/*first, check that we only have one T object in our object list: */
	if (this->num!=1) _error_("trying to delivery a single hook object when hook holds " << this->num << " objects" << "\n");

	/*check NULL: */
	if (this->objects==NULL) _error_("hook is not pointing to any object, objects pointer is NULL");

	return *objects;
}

/*}}}*/
/*FUNCTION Hook::deliverp{{{*/
Object** Hook::deliverp(void){
	return objects;
}
/*}}}*/
/*FUNCTION Hook::Ids{{{*/
int* Hook::Ids(void){
	return this->ids;
}
/*}}}*/
/*FUNCTION Hook::GetNum{{{*/
int Hook::GetNum(void){
	return this->num;
}
/*}}}*/
/*FUNCTION Hook::GetObjects{{{*/
Object** Hook::GetObjects(void){
	return this->objects;
}
/*}}}*/
/*FUNCTION Hook::GetOffsets{{{*/
int* Hook::GetOffsets(void){
	return this->offsets;
}
/*}}}*/
/*FUNCTION Hook::Spawn{{{*/
Hook* Hook::Spawn(int* indices, int numindices){

	/*output: */
	Hook* output=NULL;

	/*allocate: */
	output=new Hook();

	/*If this Hook is empty, simply return*/
	if(this->num==0){
		output->num=0;
		return output;
	}

	/*Else, check that we are requesting a half of num*/
	if(numindices>this->num) _error_("Cannot spawn hook with " << numindices << " objects from a Hook of " << this->num << " objects");

	/*go pickup the correct objects, ids and offsets :*/
	output->num=numindices;
	if(output->num<1) _error_("Trying to spawn an empty ElementProperties!");

	output->objects = xNew<Object*>(output->num);
	output->ids     = xNew<int>(output->num);
	output->offsets = xNew<int>(output->num);

	for(int i=0;i<output->num;i++){
		output->objects[i] = this->objects[indices[i]];
		output->ids[i]     = this->ids[indices[i]];
		output->offsets[i] = this->offsets[indices[i]];
	}

	return output;
}
/*}}}*/
