/*!\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 "./objects.h"
#include "../Container/Container.h"
#include "../EnumDefinitions/EnumDefinitions.h"
#include "../shared/shared.h"
#include "../include/include.h"


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

	/*Intermediaries*/
	int i;

	/*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=(Object**)xmalloc(this->num*sizeof(Object*));
		this->ids=(int*)xmalloc(this->num*sizeof(int));
		this->offsets=(int*)xmalloc(this->num*sizeof(int));

		/*Copy ids: */
		for (i=0;i<this->num;i++){
			this->ids[i]=in_ids[i];
			this->objects[i]=NULL;
			this->offsets[i]=0;
		}
	}
}
/*}}}*/
/*FUNCTION Hook::~Hook(){{{1*/
Hook::~Hook(){
	/*deallocate: */
	xfree((void**)&this->objects);
	xfree((void**)&this->ids);
	xfree((void**)&this->offsets);
	return;
}
/*}}}*/

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

	int i;
	if (num){
		printf("   Hook: \n");
		printf("      num=%i\n",this->num);
		printf("      ids: ");
		for (i=0;i<this->num;i++) printf("%i ",this->ids[i]);
		printf("\n");
		printf("      offsets: ");
		for (i=0;i<this->num;i++) printf("%i ",this->offsets[i]);
		printf("\n");
	}
	else{
		printf("   Hook: num=0 \n");
	}
}
/*}}}*/
/*FUNCTION Hook::DeepEcho{{{1*/
void Hook::DeepEcho(void){

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

	int i;

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

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

	/*copy in the fields: */
	output->num=this->num;
	if(output->num){
		output->objects=(Object**)xmalloc(output->num*sizeof(Object*));
		output->ids=(int*)xmalloc(output->num*sizeof(int));
		output->offsets=(int*)xmalloc(output->num*sizeof(int));
	}
	
	for(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{{{1*/
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]=(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_("%s%i%s%i%s"," wrong id: ",this->objects[i]->Id()," vs ",this->ids[i],"  in resolved pointer!");
		}
	}
}
/*}}}*/
/*FUNCTION Hook::delivers{{{1*/
Object* Hook::delivers(void){
	
	/*first, check that we only have one T object in our object list: */
	if (this->num!=1) _error_("%s%i%s\n"," trying to delivery a single hook object when hook holds ",this->num," objects");

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

	return *objects;
}

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

	int i;

	/*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 %i objects from a Hook of %i objects",numindices,this->num);

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

	output->objects=(Object**)xmalloc(output->num*sizeof(Object*));
	output->ids=(int*)xmalloc(output->num*sizeof(int));
	output->offsets=(int*)xmalloc(output->num*sizeof(int));

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