//
//  MCPObjectContext.h
//  MCPersistence
//
//  Created by aj on Tue Dec 25 2001.
//  Copyright (c) 2001 __MyCompanyName__. All rights reserved.
//

#import <Foundation/Foundation.h>


extern NSString *MCPObjectContextWillSaveChangesNotification;
extern NSString *MCPObjectContextDidSaveChangesNotification;

@class MCPRelationship;
@class MCPQueryEngine;
@class MCPDatabaseConnection;
@class BDQualifier;
@class MCPFetchSpecification;
@class MCPObject;
@protocol MCPUserProtocol;
@protocol MCDelegateUser;
@class MCLinkingContext;
@class MCCreationContext;
@class MCBlueprintManager;
@class MCPObjectDeletionHandler;
@class MCPOptionsCache;
@class MCSourceListManager;
@class MCPModel;

@interface MCPObjectContext : NSObject {

	NSMutableArray		         		*_insertedObjects;
	NSMutableArray		         		*_deletedObjects;
	NSMutableArray		         		*_updatedObjects;
	NSMutableSet		         		*_secondaryInsertedObjects;
	NSMutableSet		         		*_secondaryDeletedObjects;
	NSMutableSet		         		*_manyToManyPool;

	NSMutableArray		         		*_objectsToUnlock;
	
	
	id					         		_queryEngine;
	id					         		_delegate;   // not retained
	id					         		_document;   // not retained
	id                                   _mdDelegate; // not retained
	
	id <MCPUserProtocol,MCDelegateUser>  _user; // not retained
	
	BOOL					         	_disableNotifications; // useful when doing bulk stuff
	BOOL                                __isSaving;
	
	id						         	_applicableParentObject;

	BOOL                                _delegateDirtyMethod;
    
    MCLinkingContext*                   _linkingContext;
	MCCreationContext*                  _creationContext;
	MCBlueprintManager*                 _blueprintManager;
	MCPObjectDeletionHandler*           _deletionHandler;
	MCPOptionsCache                    *_optionsCache;
	MCSourceListManager*                _sourceListManager; // non-retained - lives in the document doh!

}

- (void)setDocument:(id)aDoc;
- (id)document;

// Subclassers should override this
- (MCPModel *)model;

- (void)setApplicableParentObject:(id)aParentObj;
- (id)applicableParentObject;

/*!
 Checks the model to see "aName" is a valid entity, returns NO otherwise.
 */
- (BOOL)isValidEntityName:(NSString *)aName;

- (void)setDatabaseConnection:(MCPDatabaseConnection *)connection;
- (MCPDatabaseConnection *)databaseConnection;

- (NSUndoManager *)undoManager;
- (BOOL)hasUndoManager;

- (void)setDelegate:(id)aDeleg;
- (id)delegate;

// MetaData Delegate, this guy will handle the meta data file cache for object->file inserts, deletes, and updates
- (id)mdDelegate;
- (void)setMdDelegate:(id)aMdDelegate;

- (id <MCPUserProtocol, MCDelegateUser>)user;
- (void)setUser:(id <MCPUserProtocol, MCDelegateUser>)anUser;

// CREATION CONTEXT
- (MCCreationContext *)creationContext;
- (void)setCreationContext:(MCCreationContext *)aCreationContext;
// Implemented by subclassers to return the class that should be instantiated for the creation context
// The returned class should subclass MCCreationContext
- (Class)creationContextClass;

- (MCBlueprintManager*)blueprintManager;
- (void)setBlueprintManager: (MCBlueprintManager*)aManager;

- (void)_addToDeletedObjects:(id)anObj;

//- (void)processLinkedFlagsForSavingObjects;

- (void)disableNotifications;
- (void)enableNotifications;
- (BOOL)areNotificationsEnabled;

- (BOOL)isSaving;

- (void)disableInstanceCacheRegistration;
- (void)enableInstanceCacheRegistration;

- (void)disableRemoteIncomingNotifications;
- (void)enableRemoteIncomingNotifications;


// retains the object and sets the objectContext on it
- (void)setQueryEngine:(MCPQueryEngine *)anObject;
- (id)queryEngine;

- (void)insertObject:(id)obj;
- (void)insertObjects:(NSArray *)objs;
- (NSArray *)insertedObjects;
- (NSArray*)secondaryInsertedObjects;

- (void)deleteObject:(id)obj;
- (void)deleteObjects:(NSArray *)objs;
- (NSArray *)deletedObjects;

- (void)updateObject:(id)obj;
- (NSArray *)updatedObjects;

- (void)objectWillChange:(id)obj;

- (void)object:(id)obj willAddObject:(id)aRelated toRelationshipWithKey:(NSString *)aName;
- (void)object:(id)obj didAddObject:(id)aRelated toRelationshipWithKey:(NSString *)aName;

- (void)object:(id)obj willRemoveObject:(id)aRelated fromRelationshipWithKey:(NSString *)aName;
- (void)object:(id)obj didRemoveObject:(id)aRelated fromRelationshipWithKey:(NSString *)aName;

// convenience
- (void)postChangeNotification:(NSString *)notif forParentObject:(id)parent 
		related:(id)related relationshipKey:(NSString *)aKey;


	// for a given object, we find it's flattenned relationships, for each flattened relationship, we get the inverse object and remove obj from it's arrays ...
- (void)removeObjectFromInverseRelationships:(id)obj;


- (id)objectForRelationship:(MCPRelationship *)rel inObject:(id)obj refresh:(BOOL)refresh;
- (id)objectForRelationship:(MCPRelationship *)rel inObject:(id)obj;
- (id)objectForRelationshipWithKey:(NSString *)aName inObject:(id)obj;

// subclass responsibility - attempts to find an object in memory ...
- (id)cachedObjectForEntityNamed:(NSString *)aName withPrimaryKeyValue:(id)aVal;

// just gets the first one
- (id)firstObjectForEntityNamed:(NSString *)aName matchingQualifier:(BDQualifier *)qual;
- (id)objectForEntityNamed:(NSString *)aName wherePrimaryKeyValueIs:(id)aValue;

- (NSArray *)objectsForEntityNamed:(NSString *)aName;
- (NSArray *)objectsForEntityNamed:(NSString *)aName matchingQualifier:(BDQualifier *)qual;
- (NSArray*)objectForEntityNamed:(NSString*)aName matchingQualifier:(BDQualifier*)qual excludingAttributeNames:(NSArray*)attributeNames;

// sets the object context if not set, then passes to database connection
- (NSArray *)objectsMatchingFetchSpecification:(MCPFetchSpecification *)fspec;

- (NSArray *)objectsForEntityNamed:(NSString *)aName 
				 matchingQualifier:(BDQualifier *)qual 
					 sortOrderings:(NSArray *)sorts 
						 batchSize:(unsigned int)batchSize 
					   batchNumber:(unsigned int)batchNum;

- (NSArray *)objectsForEntityNamed:(NSString *)aName 
				 matchingQualifier:(BDQualifier *)qual 
				   sortAscendingBy:(NSString *)aKey 
						 batchSize:(unsigned int)batchSize 
					   batchNumber:(unsigned int)batchNum;

- (NSArray *)objectsForEntityNamed:(NSString *)aName 
				 matchingQualifier:(BDQualifier *)qual 
				  sortDescendingBy:(NSString *)aKey 
						 batchSize:(unsigned int)batchSize 
					   batchNumber:(unsigned int)batchNum;


- (NSNumber *)objectsCountForEntityNamed:(NSString *)aName matchingQualifier:(BDQualifier *)qual;


- (NSNumber *)resultMaxForEntityNamed: (NSString*)aName onAttributeNamed: (NSString*)anAttributeName;
- (NSNumber *)resultMaxForEntityNamed:(NSString *)aName onAttributeNamed:(NSString *)anAttributeName matchingQualifier: (BDQualifier*)aQualifier;


// checks if join row exists before inserting ...
// clears faults on both flattened and to-many relationships
- (void)efficientlyJoinObject:(MCPObject *)obj usingManyToManyRelationshipWithKey:(NSString *)relName toObject:(MCPObject *)otherObject;
// Does the reverse
- (void)efficientlyUnjoinObject:(MCPObject *)obj usingManyToManyRelationshipWithKey:(NSString *)relName fromObject:(MCPObject *)otherObject;
// Returns whether the objects are already joined together
- (BOOL)efficientlyCheckJoinForObject:(MCPObject *)obj 
   usingManyToManyRelationshipWithKey:(NSString *)relName 
							 toObject:(MCPObject *)otherObject;

- (void)didRelateObject:(id)obj toObject:(id)destObj withRelationship:(MCPRelationship *)rel;

- (NSException *)tryToSaveChanges;
- (void)saveChanges;


- (void)syncQuickSearchForObjects:(NSArray *)objs;

	// attempts to lock the object
- (BOOL)lockObject:(id)anObject;

// unlock the object outright
- (void)unlockObject:(id)anObject;

// unlocks the object only after save
- (void)unlockObjectAfterSave:(id)anObject;

// unlocks an object if it is marked as unlock after save and there are no changes
// removes the object from unlockObjects array
- (void)unlockObjectAfterUse:(id)anObject;

	// does the object really exist ?? - maybe not
- (BOOL)doesObjectExist:(id)anObject;
	// must responds to lastFetchDate
- (BOOL)doesObjectRequireRefresh:(id)anObject; 

- (void)refreshObject:(id)anObject;

// Using the options cache, grabs all the options(i.e. GWCategory) for a particular key, as specified in the OptionsConfiguration.plist
- (NSArray *)optionsForKey:(NSString *)aKey;
- (id)optionForKey:(NSString *)aKey primaryKeyValue:(NSNumber *)aValue;

- (MCLinkingContext *)linkingContext;
- (void)setLinkingContext:(MCLinkingContext *)aLinkingContext;

- (MCPObjectDeletionHandler *)deletionHandler;
- (void)setDeletionHandler:(MCPObjectDeletionHandler *)aDeletionHandler;

- (MCPOptionsCache *)optionsCache;
- (void)setOptionsCache:(MCPOptionsCache *)aOptionsCache;

- (MCSourceListManager *)sourceListManager;
- (void)setSourceListManager:(MCSourceListManager *)aSourceListManager;

@end

@interface NSObject (ObjectContextDelegate)
- (BOOL)objectContext:(MCPObjectContext *)oc willUnlockObjectAfterSave:(id)obj;

- (BOOL)objectContextDidDirtyObjectStore: (MCPObjectContext*)oc;

@end

@interface NSObject (ObjectContextMDDelegate)
- (void)objectDidDelete:(id)obj;
- (void)objectDidUpdate:(id)obj;
- (void)objectDidInsert:(id)obj;
@end
