//
//  MCPDatabaseConnection.h
//  MCPersistence
//
//  Created by aj on Fri Dec 21 2001.
//  Copyright (c) 2001 __MyCompanyName__. All rights reserved.
//

#import <Foundation/Foundation.h>

#import "MCPDefines.h"
#import "MCPConnectionInfo.h"

// ABSTRACT CLASS!! - FOR FUTURE USE

@class MCPModel;
@class MCPObject;
@class MCPEntity;
@class MCPSQLExpression;
@class MCPRelationship;
@class MCPObjectContext;
@class MCPAttribute;
@class MCPInstanceCache;
@class BDQualifier;
@class MCPFetchSpecification;
@class MCPTableAliases;


extern NSString*   MCPDatabaseConnectionShutDownNotification;
extern NSString*   MCPDatabaseConnectionStartedUpNotification;


@interface MCPDatabaseConnection : NSObject {

	MCPEntity			*_currentEntity;
	MCPSQLExpression	*_defaultExpression;
	
	MCPInstanceCache	*_instanceCache;
	
	NSDictionary*        _connectionInfo;
    MCPConnectionInfo*   __databaseConnectionInfo;

	BOOL                 additionalWhereConstrantsEnabled;
	
	id					_document; // optional -- not retained!
    id                  __objectContext; // there's no need to set this unless you're at a sub-document level -- not retained

    MCPModel *model;
}

// At initialize, your subclass should set this value. Used for things like string conversion
// [[MCPDatabaseConnection currentDatabaseConnectionClass] sqlSafeStringFromString:aUnicodeString];
+ (void)setCurrentDatabaseConnectionClass:(Class)aClass;
+ (Class)currentDatabaseConnectionClass;

// this is required to be set when doing any kind of sql generation!
// to be sure you are using the correct encoding, always set it before doing things like BDQualifier
+ (void)setCurrentDatabaseEncoding:(NSStringEncoding)encoding;
+ (NSStringEncoding)currentDatabaseEncoding;

// subclass responsibility converts using the currentDatabaseEncoding
+ (NSData *)sqlSafeDataFromData:(NSData *)data;
+ (const char *)sqlSafeStringFromData:(NSData *)data;
+ (NSData *)databaseEncodedDataFromString:(NSString *)str;
+ (const char *)sqlSafeStringFromString:(NSString *)str;

- (id)initWithModel:(MCPModel *)aModel;

- (NSStringEncoding)databaseEncoding;

- (NSString*)escapeStringForSQLInclusion: (NSString*)in_str;


- (MCPModel *)model;
- (void)setModel:(MCPModel *)aModel;

// Can be used to force the connection to the database to close - this should do whatever is done on dealloc to clean up the connection without releasing any connection based objects
- (void)closeConnection;

- (BOOL)isLocalDatabase;
- (NSCalendarDate *)timeOnServer;
- (BOOL)clientServerTimeIsWithinThreshold;

// returns a UUID associated with the database. The same database should always return the same UUID. Offline databases should also return the same UUID as the master.
- (NSString *)databaseUUID;

- (NSDictionary *)connectionInfo;
- (void)setConnectionInfo:(NSDictionary *)aConnectionInfo;

-(MCPConnectionInfo *)databaseConnectionInfo;
-(void)setDatabaseConnectionInfo:(MCPConnectionInfo*)anInfo;

- (NSString *)username;
- (NSString *)password;

// replaces the current instance cache with the one in conn (be careful when using this)
// resets the instance cache in conn
- (void)takeInstanceCacheFromConnection:(MCPDatabaseConnection *)conn;

- (BOOL)connectWithConnectionDictionary:(NSDictionary *)aDict;
- (NSDictionary *)connectWithLoginPanelUsingConnectionDictionary:(NSDictionary *)aDict;

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

- (void)setObjectContext:(id)anObjContext;
- (id)objectContext;

- (id)databaseAdaptor;
- (MCPInstanceCache *)instanceCache;

- (void)setCurrentEntity:(MCPEntity *)value;
- (MCPEntity *)currentEntity;

- (void)setDefaultSQLExpression:(MCPSQLExpression *)value;
- (MCPSQLExpression *)defaultSQLExpression;

// subclasses can create a custom subclass of the sql expression. Should return an autorelease instance
- (MCPSQLExpression *)createDefaultSQLExpression;

// Returns a string that can be used to return a unique row from the database. Obj must responds to valueForAttribute:
- (NSString *)primaryKeySQLForObject:(id)obj entity:(MCPEntity *)ent;


- (NSString*)additionalWhereConstrantsForQueryType: (MCPDatabaseQueryType)aType entityNamed: (NSString*)aName tableAliases: (MCPTableAliases*)tableAliases;
  // Subclassers can override this method to return additional SQL where constraints that will be AND'ed onto the end of any existing constraint, or simply be used as the constaint if there is no existing - DL uses this to inject its permission and trash systems to all queries in the system
- (void)disableAdditionalWhereConstraints;
- (void)enableAdditionalWhereConstraints;


- (NSMutableArray *)convertRawRows:(NSArray *)rows 
	toObjectForEntity:(MCPEntity *)ent objectContext:(MCPObjectContext *)context;

- (NSMutableArray *)convertDatabaseRawRows:(NSArray *)rows toEntityRawRowsForEntity:(MCPEntity *)ent;


// Executes the SQL and returns an array of dictionaries. Each key is a column name, the object is has the corresponding value. If NULL is returned for a value NSNull are inserted into the dictionary other the key is not inserted in the dictionary
- (NSArray *)rawRowsForRawSQL:(id)rawSQL eliminateNSNulls:(BOOL)flag;


- (NSArray *)rawRowsForEntityNamed:(NSString *)aName;

- (NSArray *)rawRowsForEntityNamed:(NSString *)aName matchingQualifier: (BDQualifier*)qualifier;
- (NSArray *)rawRowsForEntityNamed:(NSString *)aName desiredAttributeNames: (NSArray*)attributeNames matchingQualifier: (BDQualifier*)qualifier;
- (NSArray *)rawRowsForEntityNamed:(NSString *)aName desiredAttributes: (NSArray*)attributes matchingQualifier: (BDQualifier*)qualifier;

- (NSArray *)rawRowsForEntityNamed:(NSString *)aName 
		withRawSQLWhere:(id)whereClause 
		order:(NSString *)orderClause;

- (NSArray *)rawRowsMatchingFetchSpecification:(MCPFetchSpecification *)fetchSpec;


- (NSArray *)rawRowsFromRelationship:(MCPRelationship *)rel 
		inRawRow:(id)dict 
		entity:(MCPEntity *)anEntity;

- (NSArray *)rawRowsFromRelationshipWithName:(NSString *)relName 
		inRawRow:(id)dict 
		entity:(MCPEntity *)anEntity;


- (NSArray *)rawRowsForEntityNamed:(NSString *)aName
                   withRawSQLWhere:(id)whereClause
                              from:(NSString *)from
                             order:(NSString *)orderClause;
							 
- (NSArray *)rawRowsForEntityNamed:(NSString *)aName
				withRawSQLWhere:(id)whereClause
				from:(NSString *)from
				groupBy:(NSString *)groupBy
				order:(NSString *)orderClause
				fetchLimit:(long)fetchLimit
				batch:(int)batchNumber
				distinctResults:(BOOL)distinct
				tableAliases:(MCPTableAliases *)tableAliases;
							 
- (NSArray *)rawRowsForEntityNamed:(NSString *)aName
				 desiredAttributes:(NSArray*)attributes
				   withRawSQLWhere:(id)whereClause
							  from:(NSString *)from
						   groupBy:(NSString *)groupBy
							 order:(NSString *)orderClause
						fetchLimit:(long)fetchLimit
							 batch:(int)batchNumber
				   distinctResults:(BOOL)distinct
					  tableAliases:(MCPTableAliases *)tableAliases;


// meant for high performance usage (i.e. combo box) returns array of dictionaries
- (NSArray *)uniqueRawRowsForEntity:(MCPEntity *)ent 
		attributes:(NSArray *)anArray 
		whereAttribute:(MCPAttribute *)attribute 
		isLike:(NSString *)val
		fetchLimit:(unsigned int)limit;

// meant for high performance usage (i.e. combo box) - returns array of strings
- (NSArray *)uniqueStringsFromEntity:(MCPEntity *)ent 
		whereAttribute:(MCPAttribute *)attribute 
		isLike:(NSString *)val
		fetchLimit:(unsigned int)limit;

// select count (*) from ... where ...
- (NSNumber *)resultCountForQualifier:(BDQualifier *)qualifier entityName:(NSString *)aName; 
- (NSNumber *)resultCountForRawSQL: (NSString*)sql; 

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


- (id)objectForEntityNamed:(NSString *)aName 
		fromRawRow:(NSDictionary *)raw 
		inContext:(MCPObjectContext *)context;


- (NSArray *)objectsForEntityNamed:(NSString *)aName inContext:(MCPObjectContext *)context;


// This is a wrapper method which calls the above with nil for the from
- (NSArray *)objectsForEntityNamed:(NSString *)aName
                         withRawSQLWhere:(id)whereClause
                                   order:(NSString *)orderClause
                               inContext:(MCPObjectContext *)context;

- (NSArray *)objectsForEntityNamed:(NSString *)aName 
				   withRawSQLWhere:(id)whereClause 
							  from:(NSString*)fromClause
							 order:(NSString *)orderClause
						 inContext:(MCPObjectContext *)context;

- (id)firstObjectForEntityNamed:(NSString *)aName 
	matchingQualifier:(BDQualifier *)qualifier inContext:(MCPObjectContext *)context;

- (NSArray *)objectsForEntityNamed: (NSString *)aName
                 matchingQualifier: (BDQualifier *)qualifier
                         inContext: (MCPObjectContext *)context;
- (NSArray *)objectsForEntityNamed: (NSString *)aName matchingQualifier: (BDQualifier *)qualifier excludingAttributeNames:(NSArray*)attributeNames inContext: (MCPObjectContext *)context;


- (NSArray *)objectsMatchingFetchSpecification:(MCPFetchSpecification *)fetchSpec;
						 
	
// throws expection if entity have more than one primary key
- (id)objectForEntity:(MCPEntity *)ent 
	wherePrimaryKeyValueIs:(id)aValue inContext:(MCPObjectContext *)context;
	
- (id)objectForEntityNamed:(NSString *)aName 
	wherePrimaryKeyValueIs:(id)aValue inContext:(MCPObjectContext *)context;



// returns an autoreleased mutable array or an autoreleased object depending on the type of relationship
- (id)objectFromRelationshipWithName:(NSString *)relName 
		inObject:(id)obj 
		inContext:(MCPObjectContext *)context;

- (id)objectFromRelationship:(MCPRelationship *)rel 
		inObject:(id)obj 
		inContext:(MCPObjectContext *)context
		refresh:(BOOL)refresh;

- (id)objectFromRelationship:(MCPRelationship *)rel 
		inObject:(id)obj 
		inContext:(MCPObjectContext *)context;

- (id)inMemoryObjectFromRelationship:(MCPRelationship *)rel 
		inObject:(id)obj 
		inContext:(MCPObjectContext *)context;

- (id)inMemoryObjectFromToOneRelationship:(MCPRelationship *)rel 
								 inObject:(id)obj 
								inContext:(MCPObjectContext *)context;

		
- (NSException *)permanentlyDeleteObject:(id)obj;		


// fetches the raw row, throws an exception if the row is not found, update the object and asks all relationship to refreshed
- (void)refreshObject:(MCPObject *)obj;


- (NSException *)updateRawRow:(NSDictionary *)raw 
	changedAttributeNames:(NSArray *)changed forEntity:(MCPEntity *)entity;
	
	
- (NSException *)insertRawRow:(NSDictionary *)raw forEntity:(MCPEntity *)entity;


- (NSException *)deleteRawRow:(NSDictionary *)raw forEntity:(MCPEntity *)entity;
	
	
- (NSString *)createNewIDForAttribute:(MCPAttribute *)attrib entity:(MCPEntity *)entity;

// ---------------------------------------------------------------
// raw table equivalents
// ---------------------------------------------------------------

- (NSException *)updateRawRow:(NSDictionary *)raw forTableNamed:(NSString *)aTable 
		where:(id)whereStatement blobKeys:(NSArray *)blobKeys;
		
- (NSException *)updateRawRow:(NSDictionary *)raw forTableNamed:(NSString *)aTable 
		whereKeyName:(NSString *)whereKey blobKeys:(NSArray *)blobKeys;
	
	
- (NSException *)insertRawRow:(NSDictionary *)raw forTableNamed:(NSString *)aTable 
		blobKeys:(NSArray *)blobKeys;

- (NSException *)deleteRawRow:(NSDictionary *)raw forTableNamed:(NSString *)aTable 
		where:(id)whereStatement;
		
- (NSException *)deleteRawRow:(NSDictionary *)raw forTableNamed:(NSString *)aTable 
		whereKeyName:(NSString *)whereKey;
	
	
- (NSString *)createNewIDForColumnName:(NSString *)coln forTableNamed:(NSString *)aTable;

- (BOOL)doesJoinRawRowWithColumn:(NSString *)columnOne value:(id)valueOne
                    secondColumn:(NSString *)columnTwo value:(id)valueTwo
               existInTableNamed:(NSString *)tableName;

- (BOOL)doesRowID:(id)rowid existInTableNamed:(NSString *)tableName;

// pass a dictionary with just the compound key! This method will get the appropriate primary key if required
- (NSException *)insertJoinRawRow:(NSMutableDictionary *)joinDict forTableNamed:(NSString *)aTable;

// ---------------------------------------------------------------


// ---------------------------------------------------------------
// Database script support
// ---------------------------------------------------------------

/*!
	@method executeDatabaseScript:
	@discussion this method gives you the opportunity to execute raw SQL script. It is the subclass's responsibility to implement. the subclass must support embedded objc-c calls, so that Objc can be performed between individual scripts segments
	@result Nil is all is good. Array or error strings if there is a problem.
	@param script the script to be executed (i.e. raw SQL)
*/
- (NSArray *)executeDatabaseScript:(NSString *)script;

// ---------------------------------------------------------------

- (NSException *)executeStringSQL:(NSString *)sql;



- (BOOL)lockObject:(id)anObject;
- (void)unlockObject:(id)anObject;

/* used to determine is an object is really in the database. For example - it was deleted by someone else but we didn't know about the delete */
- (BOOL)doesObjectExist:(id)anObject;

	// must responds to lastFetchDate
- (BOOL)doesObjectRequireRefresh:(id)anObject; 

- (void)prepareForRelease;

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

// does a quick backup of the database to the standard backup location
- (NSException *)performQuickDatabaseBackup;
- (NSException *)performDatabaseCleanupAndBackup;


// -----------------------------------------------------------------------------
// Database User Support
- (BOOL)addDatabaseUserWithName:(NSString *)username password:(NSString *)password;
- (BOOL)removeDatabaseUserWithName:(NSString *)username;
- (BOOL)setNewPassword:(NSString *)password forDatabaseUserWithName:(NSString *)username;
- (BOOL)setNewUsername:(NSString *)username forDatabaseUserWithName:(NSString *)oldUsername;


// -----------------------------------------------------------------------------
// WAL checkpoint support for SQLite - SQLite subclass must implement at a minimum
- (BOOL)checkpoint;

@end


/*
Methods called on the document
*/
@interface NSObject (MCDatabaseConnectionDocumentCalls)


- (BOOL)databaseConnection:(id)aConn shouldProcessRemoteInsertForEntity:(MCPEntity *)entity;

- (BOOL)databaseConnection:(id)aConn 
	willProcessRemoteInsertForRawRow:(NSMutableDictionary *)raw 
	entity:(MCPEntity *)entity;

- (void)databaseConnection:(id)aConn gotRemoteUpdateForObject:(MCPObject *)obj;
- (void)databaseConnection:(id)aConn gotRemoteInsertForObject:(MCPObject *)obj;
- (void)databaseConnection:(id)aConn gotRemoteDeleteForObject:(MCPObject *)obj;

- (void)databaseConnection:(id)aConn gotRemoteLockForObject:(MCPObject *)obj;
- (void)databaseConnection:(id)aConn gotRemoteUnlockForObject:(MCPObject *)obj;


@end
