/*
   Project: Adun

   Copyright (C) 2005 Michael Johnston & Jordi Villa-Freixa

   Author: Michael Johnston

   This application is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This application is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#ifndef _ADSYSTEMNODE_
#define _ADSYSTEMNODE_

#include "AdunKernel/AdDelegation.h"
#include "AdunKernel/AdDataSources.h"
#include "AdunKernel/AdUpdating.h"
#include "AdunKernel/AdunObject.h"
#include "AdunKernel/AdunInteractionSystem.h"
#include "AdunKernel/AdunSystem.h"
#include "AdunKernel/AdunRelationship.h"
#include "AdunKernel/AdFrameworkFunctions.h"


/**
Manages a collection of AdSystems & AdInteractionSystems, 
and the relationships between them. There are two possible relationships types
"interacts" and "contains" and every pairwise relationship is represented by an AdRelationshipObject.
For every AdSystem that interacts with another an AdInteractionSystem is created.
For every AdSystem that contains others AdSystemNode inserts them into it - 
this requires that the containing AdSystems dataSource supports insertion by implementing
the setExclusionPoints:radius method.

In addition AdSystemNode also keeps track of the status (See AdSystem and AdInteractionSystem for the
allowed states) of each of the consituent systems. When the status of a subsystem changes AdSystemNode 
updates the active, inactive and passive lists taking into account interaction systems that are affected by 
the change i.e. if an AdSystem status becomes "inactive" it will have the effect of changing the 
status to "inactive" of any AdInteractionSystems that it was involved in. It will then post an
AdSystemStatusDidChangeNotification (see below for more information).

Notifications

AdSystemNode posts an AdSystemStatusDidChangeNotification for the original subsystem whose status changed.
The object of the notification is the AdSystemNode and it contains a userInfo dict with the
following self explanatory keys: @"Subsystem", @"PreviousStatus", @"CurrentStatus". 
Note no notification will be sent for subsequent changes involving AdInteractionSystems. 
If necessary, objects that observe this notification should use the "interactionSystemsForAdSystem:" method to
find which interaction systems were affected (only necessary when a AdSystem becomes inactive).

\todo On creation of the systems and before creating the interactions AdSystemNode should remove
all translational degrees of freedom.
\todo Add "rigid" as a system status

**/

@interface AdSystemNode: AdObject <AdUpdating, AdFrameUpdating>
{
	NSMutableArray* systems;
	NSMutableArray* systemRelationships;
	NSMutableDictionary* systemNames;
	//status ivars
	NSMutableArray* activeSystems;
	NSMutableArray* passiveSystems;
	NSMutableArray* inactiveSystems;
	NSMutableArray* activeAdSystems;
	NSMutableDictionary* statusDict;
	//relationship ivars
	NSMutableArray* interactionDataSources;
	NSMutableArray* interactionSystems;
	NSMutableDictionary* systemRelationshipsDict; 	//<! Keys: AdSystem names Values: related relationships
	NSMutableArray* allowedRelationships;
	NSMutableDictionary* derivedSystemsDict;
}

- (id) initWithSystems: (NSArray*) arrayOne relationships: (NSArray*) arrayTwo;
//temporary method until AdSystemNodes environment initialisation is worked out
- (id) initWithSystems: (NSArray*) arrayOne relationships: (NSArray*) arrayTwo environment: (id) object;
/**
Returns an array containing the current active systems
*/
- (NSArray*) activeSystems;
/**
Returns an array containing the current passive systems
*/
- (NSArray*) passiveSystems;
/**
Returns an array containing the current inactive systems
*/
- (NSArray*) inactiveSystems;
/**
Returns an array containing all the systems
*/
- (NSArray*) allSystems;
/**
Returns an array of the systems of type \e type who have status \e status or an empty array
if nothing matches the criteria.
\param type Either "Standard" or "Interaction"
\param status One of "Active", "Passive"  or "Inactive".
If either \e type or \e status  is not one of the allowed values an NSInvalidArgumentException
is raised. 
*/
- (NSArray*) systemsOfType: (NSString*) type withStatus: (NSString*) status;
/**
Remove the system whose name is \e aName from the node.
This also removes all relationships the system was involved with.
The behaviour of containers when a system they contain is removed
is object dependant i.e. wheather it fills the space left or not.
\todo expand on this aspect
*/
- (void) removeSystemWithName: (NSString*) aName;
/**
Returns the system whose name is \e aName
*/
- (id) systemWithName: (NSString*) aName;
/**
Adds AdSystem object to the node (and hence the simulation) and
creates all relationships defined by the relationships array. The relationships
must all contain the added system and one other system already present in the
node. If not an NSInvalidArgumentException is raised. Passing nil for \e relationships
creates a system who does not interact with anything.
The system name attribute must be unique i.e. no other system in the node can have
the same name. If it is not unique an NSInvalidArgumentException is raised.
*/
- (void) addSystem: (AdSystem*) system withRelationships: (NSArray*) relationships;
/**
Removed the relationship \e aRelationship from the node. If any interaction
system were created they will be destroyed. If the relationship is invalid
an NSInvalidArgumentException is raised. Does nothing if \e aRelationship is nil
\todo containers
*/
- (void) removeRelationship: (AdRelationship*) aRelationship;
/**
Adds the relationship to the node. The relationship must involve system
currently in the node otherwise NSInvalidArgumentException is raised
*/
- (void) addRelationship: (AdRelationship*) aRelationship;
/**
Returns an array of all the relationships involving system with name \e aName.
Raises NSInvalidArgumentException if a system with name \e aName does
not exist
*/
- (NSArray*) relationshipsForSystemWithName: (NSString*) aName;
/**
Returns an array of the relationships involving system with name \e aName
of type \e relationshipType. Type can be "interacts", "contains" and "contained"
(the later being the inverse of "contains")
*/
- (NSArray*) relationshipsForSystemWithName: (NSString*) aName ofType: (NSString*) relationshipType;
/**
Returns the interaction system associated with the "Interacts" relationship 
represented by \e relationshipObject.
If the relationship type is not "Interacts" this method returns nil.
This method also returns nil if \e relationshipObject is not present in the node
or if it is nil.
*/
- (id) interactionSystemForRelationship: (AdRelationship*) relationshipObject;
/**
Returns the relationship object associated with \e interactionSystem.
*/
- (id) relationshipForInteractionSystem: (AdInteractionSystem*) interactionSystem;
/**
Returns YES is \e aRelationship is present in the node (as determined
by isEqual:). NO otherwise.
\todo Currently checks if the two objects are exactly the same. Will implment
an isEqual for AdRelationship objects which compares based on subject objects and relationship
later.
*/
- (BOOL) containsRelationship: (AdRelationship*) aRelationship;
- (NSArray*) allRelationships;
@end

#endif
