/******************************************************************************
 **
 **	C++ Class Library for the Amiga© system software.
 **
 **	Copyright (C) 1994 by Armin Vogt  **  EMail: armin@uni-paderborn.de
 **	All Rights Reserved.
 **
 **	$Source: apphome:APlusPlus/RCS/libsource/IntuiObject.cxx,v $
 **	$Revision: 1.5 $
 **	$Date: 1994/05/08 13:48:21 $
 **	$Author: Armin_Vogt $
 **
 ******************************************************************************/


#include <APlusPlus/intuition/IntuiObject.h>
#include <APlusPlus/intuition/IntuiRoot.h>


/*------------------------- IConstraints methods --------------------------------*/

volatile static char rcs_id[] = "$Id: IntuiObject.cxx,v 1.5 1994/05/08 13:48:21 Armin_Vogt Exp Armin_Vogt $";


LONG IntuiObject::newConstraint(Tag onChangedTag,IntuiObject *noticeThis,Tag mapToTag)
{
	MapArray *dTable;
	if (NULL==(dTable=(MapArray*)cTable.find(onChangedTag)))
		cTable[onChangedTag] = dTable = new MapArray;
		
	(*dTable)[mapToTag] = noticeThis;
	ULONG dataStore;
	return getAttribute(onChangedTag,dataStore);
}

void IntuiObject::releaseObject(IntuiObject *obj)
{
}

void IntuiObject::changedAttrs(AttrList& attrs)
{
	AttrIterator tags(attrs);
	while (tags())
	{
		MapArray *dTable;
		if (NULL!=(dTable=(MapArray*)cTable.find(tags.tag())))
		{
			MapArrayIterator next(*dTable);
			IntuiObject *iob;
		
			while (NULL != (iob=(IntuiObject*)next())) 
			{
				if (!(iob->setAttributesIsRecurrent))
					iob->setAttributes(AttrList(next.key(),tags.data(),TAG_END) );
			}
		}
	}		
}

/*------------------------- IntuiRoot methods --------------------------------*/

// initialise global static variable
IntuiRoot *IntuiRoot::APPIntuiRoot = NULL;


IntuiRoot::IntuiRoot() : ScreenC(NULL,(UBYTE*)NULL)
{
   iob_count = 0;
   _dout("IntuiRoot initialised.\n");
}

IntuiRoot::~IntuiRoot()
{
   _dout("IntuiRoot::~IntuiRoot()\n");

   FOREACHSAFE(IntuiObject,this)
   {
      _dout("kill "<<node->status()<<" at "<<(long)node<<" / ");
      delete node;
      _dout("\tkilled.\n");
   }
   NEXTSAFE
   _dout("IntuiRoot::~IntuiRoot() done.\n");
	
	/** This may prevent executing setAttributes() on a deleted object.
	 ** But its the sendNotification() routine duty to check for the integrity of each 
	 ** addressed IntuiObject before calling setAttributes on it. 
	 ** Otherwise this may become a source of random enforcer hits when the deleted
	 ** object's memory  may already have been overwritten or not!
	 **/	
	setAttributesIsRecurrent = TRUE;
}

BOOL IntuiRoot::APPinitialise(int argc, char *argv[])
   /* MUST be called before creating any APP Intuition classes.
   */
{
   APPIntuiRoot = new IntuiRoot;
   if (APPOK(APPIntuiRoot))
   {
		_dout("IntuiRoot initialised.\n");
		return TRUE;
   }
	else return FALSE;
}

void IntuiRoot::APPexit()
{
   if (APPIntuiRoot)
   {
      delete APPIntuiRoot;
      APPIntuiRoot = NULL;
   }
}


/*------------------------- IntuiObject methods ------------------------------*/

IntuiObject::IntuiObject(IntuiObject *owner,const AttrList& attrs)
	: attrList(attrs)
{		
   _dout("IntuiObject::IntuiObject()\n");
	
	iTransponder = NULL;
   setAttributesIsRecurrent = FALSE;

   setIOType(INTUIOBJECT_CLASS);

   if (owner==OWNER_ROOT)
   {
      if (IntuiRoot::APPIntuiRoot)
         IntuiRoot::APPIntuiRoot->addTail(this);
   }
   else
   {
      //_dout("\towner is class"<<owner->ID()<<endl);
      if (owner->isKindOfIntuiObject())
         owner->addTail(this);
      else
      {
         if (IntuiRoot::APPIntuiRoot)
            IntuiRoot::APPIntuiRoot->addTail(this);
         cerr << "IntuiObject: owner is not derived from IntuiObject!\n";
      }
   }

   if (IntuiRoot::APPIntuiRoot)
      IntuiRoot::APPIntuiRoot->iob_count++;

   IObject() = NULL;    // initialise for safety
	
	processAttrs(attrList);
	
	_dout(".IntuiObject(" << status() << ")\n" );
}

void IntuiObject::processAttrs(AttrList& attrs)
{
	AttrManipulator next(attrs);
	
	if (next.findTagItem(IOB_ITransponder))	// look out for itransponder attachment
	{
		iTransponder = (ITransponder*)next.data();
	}

	next.reset();

	while (next.findTagItem(IOB_CnstSource))		// look out for constraint definition
	{
		IntuiObject *sourceIOB = (IntuiObject*)next.data();
		if (APPOK(sourceIOB))
		{
			if (next())
				if (next.tag()==IOB_CnstTag)	// following tag must be constraint source tag specifier
			{				
				Tag sourceTag = next.data();
				// initialise own tag data from constraint source
				if (next())
					next.writeData( sourceIOB->newConstraint(sourceTag,this,next.tag()) );
			}
		}
		else _ierror(INTUIOBJECT_CONSTRAINTSOURCE_NO_IOB);
	}
}

IntuiObject::~IntuiObject()
{		
   _dout("IntuiObject::~IntuiObject() kill childs..\n");
   FOREACHSAFE(IntuiObject, this)
   {
      _dout("kill IntuiObject..\n");
      delete node;
      _dout("\tkilled.\n");
   }
   NEXTSAFE
	
	{	
	MapArrayIterator next(cTable);
	MapArray *dTable;
	while (NULL != (dTable = (MapArray*)next())) delete dTable;
	}	
   
	_dout("IntuiObject::~IntuiObject() done.\n");
   IObject() = NULL;
   if (IntuiRoot::APPIntuiRoot)  IntuiRoot::APPIntuiRoot->iob_count--;
}


IntuiObject *IntuiObject::findRootOfKind(LONG basetype)
   /* get the first object upwards searching in the tree
      that is a kind of the given basetype.
   */
{
   IntuiObject *io = this;

   /* this loop terminates when io addresses the IntuiRoot. Its MinNodeC is not linked
      into a list and therefore io->findList() will return NULL. */
   while (NULL != (io = io->findOwner()))
      if (io->isKindOf(basetype)) return io;

   return NULL;
}


IntuiObject *IntuiObject::findRootOfType(LONG type)
   /* get the first object upwards searching in the tree
      that matches the given IntuiObject type.
   */
{
   IntuiObject *io = this;
   type |= INTUIOBJECT_CLASS;
   /* this loop terminates when io addresses the IntuiRoot. Its MinNodeC is not linked
      into a list and therefore io->findList() will return NULL. */
   while (NULL != (io = io->findOwner()))
      if (io->isType(type)) return io;

   return NULL;
}

ULONG IntuiObject::setAttributes(AttrList& attrs)
   /* Set attribute tags specified in the given taglist to their corresponding new values
      and start notifying other IntuiObjects via ITransponder and Constraints.
		Return 0L if setAttributes has been called recurrently.
   */
{
   if (!setAttributesIsRecurrent)   // check for a notification loop
   {
      _dout("IntuiObject::setAttributes("<<attrs<<")\n");
		processAttrs(attrs);
				      
		attrList.updateAttrs(attrs);	// apply changes to Attribute Taglist

		setAttributesIsRecurrent = TRUE;
		// a setAttributes call within sendNotification would be recurrent now!
		changedAttrs(attrs);		// notify constraint destinations
		if (iTransponder)
	   {     
			_dout(" IntuiObject::sendNotify("<<attrs<<"\n");
			iTransponder->sendNotification(attrs);      // attrs will be changed
   	}
					
		setAttributesIsRecurrent = FALSE;
   	return 1L;
	}
	else return 0L;
}

ULONG IntuiObject::getAttribute(Tag tag,ULONG& dataStore)
{
	return (dataStore=intuiAttrs().getTagData(tag,0));
}

ostream& operator << (ostream& OS,IntuiObject *iob)
{
   OS << "status="<<(LONG)iob->status()<<", attrList: "<<iob->intuiAttrs();
   return OS;	
}

/*--------------------- IOBrowser methods ------------------------------------*/

IOBrowser::IOBrowser(IntuiObject *start)
   /* Initialises a search object that iteratively visits all IntuiObjects dependend from
      the start IntuiObject. Note, that IntuiObjects on the same hierarchy level as
      start are not visited.
   */
{
   sp = 0;
   push((IntuiObject*)start->head());
   _dout("IOBrowser created: "<<IntuiRoot::APPIntuiRoot->getIOBCount()<<" intuiobjects. \n");
}


IntuiObject *IOBrowser::getNext(LONG type)
   /* visits the next IntuiObject in the deep first search through the subtree of 
		the start object the IOBrowser was initialised with that is a kind of the 
		given IntuiObject type. Subsequent getNext() calls return all IntuiObjects
		in the start object's subtree that are kind of the given IOTYPE except subtrees
		that have a root object of some other kind. The whole subtree is leaved out.
   */
{
   IntuiObject *root;

   do

   {
      root = pop();  // get the next node.

      if (root==NULL)
      {
          return NULL;
      }
      else
      {
         if (root->succ()) push((IntuiObject*)root->succ());
         // after the root subtree is empty, the successor will be visited

         if (root->isKindOf(type) && root->head()) push((IntuiObject*)root->head());
         // descent with the next getNext() into the subtree ONLY if the root of the 
			// subtree matches the demanded IOTYPE.
      }
   }
   while ( !(root->isKindOf(type)));
   return root;
}

