#include "windows.h"
#include "Macro.h"
#include "CObjLink.h"

int CObjLink::activationID=0;

//CObjLink::CObjLink( int (*flushObject)(CObjLink *) )
CObjLink::CObjLink()
{
	/*this->flushObject = flushObject;*/
	this->linkedObjectsList = NULL;
	this->lastActivationID = -1;
}

Link *addList( Link *list, Link *newLink )
{
	newLink->next = list;

	return newLink;
}

Link *removeList( Link *list, CObjLink *target, int type )
{
	if ( list == NULL ) return NULL;

	if ( list->object == target && list->type == type )
	{
		Link *result = list->next;
		free( list );
		return result;
	}
	else
	{
		list->next = removeList( list->next, target, type );
		return list;
	}
}

CObjLink::~CObjLink ()
{
	while ( this->linkedObjectsList != NULL )
	{
		this->linkedObjectsList->object->unlink( this, this->linkedObjectsList->type );
		this->linkedObjectsList = removeList( this->linkedObjectsList, this->linkedObjectsList->object, this->linkedObjectsList->type );
	}
}

int isInList( Link *list, CObjLink *target, int type )
{
	if ( list == NULL ) return 0;

	if ( list->object == target && list->type == type )
		return 1;
	else
		return isInList( list->next, target, type );
}

int CObjLink::isLinkedTo( CObjLink *target )
{
	int i = 0;

	while ( i < NBER_OF_LINKS )
	{
		if ( isInList( this->linkedObjectsList, target, (1<<i) ) )
			return 1;
		i++;
	}

	return 0;
}


// creation d'un lien uni-directionnel
int CObjLink::link( CObjLink *target, int type )
{
	Link *newLink;
	
	if ( !isInList( this->linkedObjectsList, target, type ) )
	{
		newLink = (Link *)malloc( sizeof(Link) );

		newLink->next = NULL;
		newLink->object = target;
		newLink->type = type;

		this->linkedObjectsList = addList( this->linkedObjectsList, newLink );

		return 1;
	}
	
	return 0;
}

// liberation d'un lien uni-directionnel
int CObjLink::unlink( CObjLink *target, int type )
{
	if ( isInList( this->linkedObjectsList, target, type ) )
	{
		this->linkedObjectsList = removeList( this->linkedObjectsList, target, type );
		return 1;
	}

	return 0;
}

int CObjLink::supports( int type )
{
	return 0;
}

//envoi d'une activation sur tous les objets liés
void CObjLink::broadcast(Link *list, int id, CObjMessage* msg)
{
	if (list != NULL)
	{
		//vérifie si l'objet courant est interessé par ce type de message
		if (list->type == msg->GetType())
			list->object->activate(id, msg);
		//propage le message aux autres objets liés
		broadcast(list->next, id, msg);
	}
}

//une modification succeptible d'interesser les objets liés a eu lieu
int CObjLink::notify(CObjMessage* msg)
{
	//marque la modification d'un ID unique
	this->activationID++;

	//propage le message aux objets liés
	this->lastActivationID = this->activationID;
	broadcast(this->linkedObjectsList, this->activationID, msg);

	return 0;
}

//reception de l'activation d'un lien venat d'un objet lié
int CObjLink::activate(int id, CObjMessage* msg)
{
	//vérifie si le message n'a pas déjà été reçue (détection des boucles)
	if (this->lastActivationID != id)
	{
		this->lastActivationID = id;
		//déclenche le traitement du message (appel de la méthode des classes dérivées)
		handle(msg);
		//propage le message aux objets liés
		broadcast(this->linkedObjectsList, id, msg);
	}

	return 0;
}

// prise en charge de la reception d'une activation d'un lien venant d'un objet lié
// par défaut aucun traitement, c'est en surchargeant qu'on obtient une gestion spécifique
int CObjLink::handle(CObjMessage* msg)
{
	return 0;
}


CObjLink *GetObjLink(mmachine m,int p_obj)
{
	return (CObjLink *)MMfetch( m, MTOP(MMfetch(m,p_obj,3)), 0 );
}

// prise en charge du repaint pour les composants lies
void CObjLink::repaint( int nberOfLinkEventsToConsider )
{
	int i = 0;
	int *id_list = (int *)malloc( sizeof(int) * ( nberOfLinkEventsToConsider + 1 ) );

	while ( i < nberOfLinkEventsToConsider )
	{
		id_list[i] = this->activationID - i;
		i++;
	}
	id_list[i] = -1;

	this->lastActivationID = -1; // on ne se repeind pas soi-meme
	broadcast_repaint( this->linkedObjectsList, id_list );

	free( id_list );
}

void CObjLink::broadcast_repaint( Link *list, int *id_list )
{
	if ( list != NULL )
	{
		list->object->repaint_linked_object( id_list );
		broadcast_repaint( list->next, id_list );
	}
}

void CObjLink::repaint_linked_object( int *id_list )
{
	int i = 0;

	if ( this->lastActivationID != -1 )
		while ( id_list[i] != -1 )
		{
			if ( id_list[i] == this->lastActivationID )
			{
				/*this->flushObject( this );*/
				this->Redraw();
				this->lastActivationID = -1;
				broadcast_repaint( this->linkedObjectsList, id_list );
				return;
			}
			i++;
		}
}

void CObjLink::Redraw()
{
	0;
}

/*******************************************************************************************************************/
/* Les fonctions Scol du Link                                                                                      */
/*******************************************************************************************************************/

int _CRnodeLink( mmachine m )
{
	int p_comp1, p_comp2, lnk_type;
	CObjLink *obj1, *obj2;

	lnk_type = MTOI( MMpull( m ) );
	p_comp2 = MTOP( MMpull( m ) );
	p_comp1 = MTOP( MMget( m, 0 ) );

	if ( p_comp1 == NIL || p_comp2 == NIL || p_comp1 == p_comp2 )
	{
		MMset( m, 0, ITOM( 0 ) );
		return 0;
	}

	if ( (obj1=GetObjLink(m,p_comp1)) == NULL || (obj2=GetObjLink(m,p_comp2)) == NULL )
	{
		MMechostr(MSKTRACE,"_CRnodeLink: Object already destroyed.\n");
		MMset( m, 0, ITOM( 0 ) );
	}
	else
	{
		int i = 0;
		int result = 0;
		while ( i < NBER_OF_LINKS )
		{
			if ( lnk_type & (1<<i) )
			{
				if ( !obj1->supports( (1<<i) ) || !obj2->supports( (1<<i) ) )
				{
					MMechostr(MSKTRACE,"_CRnodeLink: Link type not supported by components.\n");
					result = result|(1<<i);
				}
				else
				{
					obj1->link( obj2, (1<<i) );
					obj2->link( obj1, (1<<i) );
				}
//					MMset( m, 0, ITOM( obj1->link( obj2, (1<<i) ) && obj2->link( obj1, (1<<i) ) ) );
			}
			i++;
		}
		MMset( m, 0, ITOM( result ) );
	}
	return 0;
}

int _DSnodeLink(mmachine m)
{
	int p_comp1, p_comp2, lnk_type;
	CObjLink *obj1, *obj2;

	lnk_type = MTOI( MMpull( m ) );
	p_comp2 = MTOP( MMpull( m ) );
	p_comp1 = MTOP( MMget( m, 0 ) );

	if ( p_comp1 == NIL || p_comp2 == NIL || p_comp1 == p_comp2 )
	{
		MMset( m, 0, ITOM( 0 ) );
		return 0;
	}

	if ( (obj1=GetObjLink(m,p_comp1)) == NULL || (obj2=GetObjLink(m,p_comp2)) == NULL )
	{
		MMechostr(MSKTRACE,"_CRnodeLink: Object already destroyed.\n");
		MMset( m, 0, ITOM( 0 ) );
	}
	else
	{
		int i = 0;
		int result = 0;
		while ( i < NBER_OF_LINKS )
		{
			if ( lnk_type & (1<<i) )
				if ( !obj1->unlink( obj2, (1<<i) ) || !obj2->unlink( obj1, (1<<i) ) )
					result = result|(1<<i);
			i++;
		}
//		MMset( m, 0, ITOM( obj1->unlink( obj2, lnk_type ) && obj2->unlink( obj1, lnk_type ) ) );
		MMset( m, 0, ITOM( result ) );
	}

	return 0;
}
