//
//  MCReplicaClient.h
//  MCClientServer
//
//  Created by Brent Gulanowski on 10-02-23.
//  Copyright 2010 Marketcircle Inc. All rights reserved.
//


#import <MCFoundation/MCPDatabaseConnection.h>

#import <MCClientServer/MCClientServerDefines.h>

#import <Foundation/Foundation.h>

@class MCPGDatabase;
@class MCSQLiteContext;

typedef enum {
	kMCSRServerSyncStateNewer   = -1,
	kMCSRServerSyncStateSame    =  0,
	kMCSRServerSyncStateOlder   =  1,
	kMCSRServerSyncStateConflict = 2
} MCSRSyncStateComparison;


extern NSString *MCSRCreateMasterChangeLogTableSQL;


extern NSString *kMCSReplicaUUIDKey;
extern NSString *kMCSReplicaUserNameKey;
extern NSString *kMCSReplicaMasterURLKey;
extern NSString *kMCSReplicaEnvChangeDateKey;


@class MCAccessClient, MCSServiceResolver;
@class MCPDatabaseConnection, MCPDatabaseConnection;
@class MCSyncEvent;
@class MCReplicaConflictCandidate;

@interface MCReplicaClient : NSObject {

	MCAccessClient *accessClient;
	MCSServiceResolver *cloudResolver;
	MCPDatabaseConnection *remoteConn;
	MCPDatabaseConnection *localConn;
	NSString *notificationIdentifier;
	
	NSDate *startDate;
	NSString *serverChangeLogQuery;
	NSString *clientChangeLogQuery;
	
	NSArray *serverAddresses;
	NSString *connectedAddress;
	NSArray *authServerAddresses;
	unsigned authServerPort;
	NSString *databaseName;
	NSString *clientUsername;
	NSString *clientPassword;
	NSString *cloudUsername;
	NSString *replicaUUID;
	NSNumber *replicaOffset;
	
	MCSyncEvent *upstreamSyncEvent;
	MCSyncEvent *downstreamSyncEvent;
	
	NSMutableArray *serverChangeLogs;
	NSMutableArray *clientChangeLogs;
	
	MCSAuthorizationResultCode authResult;
	
	NSTimeInterval progressInterval;
	NSTimeInterval lastProgressUpdate;
	NSMutableDictionary *progressUserInfo;
	unsigned long long clientChanges;
	unsigned long long serverChanges;
	unsigned long long rowsProcessed;
	
    NSMutableSet *conflictCandidates;
    NSMutableArray *remoteRefreshCandidates; // array of dictionaries to be sent with finish notif
    
	NSDictionary *syncInfo;
	
	NSDate *lastSyncDate;
	NSDate *envChangeDate;
	NSDate *trialExpiryDate;
	
	NSTimeInterval lastChange;

	BOOL recoverOnReset;
	BOOL isMirror;
	BOOL isFinished;
	BOOL recoveryMode;
	BOOL didAuth;
	
	int exitCode;
}

@property (retain)    MCAccessClient *accessClient;
@property (retain)    MCSServiceResolver *cloudResolver;
@property (retain)    MCPDatabaseConnection *remoteConn;
@property (nonatomic, retain)    MCPDatabaseConnection *localConn;

@property (nonatomic, retain)    NSString *notificationIdentifier;

@property (copy)      NSArray *serverAddresses;
@property (copy)      NSArray *authServerAddresses;
@property (copy)      NSString *connectedAddress;
@property (readwrite) unsigned authServerPort;
@property (copy)      NSString *databaseName;
@property (copy)      NSString *clientUsername;
@property (nonatomic, copy)      NSString *cloudUsername;
@property (copy)      NSString *clientPassword;
@property (copy)      NSString *replicaUUID;
@property (nonatomic, copy)      NSNumber *replicaOffset;

@property (retain)    MCSyncEvent *upstreamSyncEvent;
@property (retain)    MCSyncEvent *downstreamSyncEvent;
@property (retain)    NSMutableArray *serverChangeLogs;
@property (retain)    NSMutableArray *clientChangeLogs;

@property (readwrite) MCSAuthorizationResultCode authResult;

@property (readwrite) NSTimeInterval progressInterval;
@property (readwrite) NSTimeInterval lastProgressUpdate;
@property (nonatomic, retain)    NSMutableDictionary *progressUserInfo;
@property (readonly) unsigned long long serverChanges;
@property (readonly) unsigned long long clientChanges;
@property (readwrite) unsigned long long rowsProcessed;

@property (retain) NSDictionary *syncInfo;
@property (nonatomic, copy)      NSDate *lastSyncDate;
@property (copy)      NSDate *envChangeDate;
@property (nonatomic, copy) NSDate *trialExpiryDate;

@property (readwrite) NSTimeInterval lastChange;

@property (readwrite) BOOL recoverOnReset;
@property (readwrite) BOOL isMirror;
@property (readwrite) BOOL finished;
@property (readwrite) BOOL recoveryMode;

@property (readwrite) int exitCode;

@property (readonly) NSString *replicaDatabaseName; // dynamic

/*!
 Conveniences to figure out if there are changes before we go ahead and do other heavy lifting.
 */
+ (unsigned long long)numberOfPendingChangesInRemoteDatabase:(MCPGDatabase *)serverDB sinceLastSyncDate:(NSDate *)aLastSyncDate forReplicantUUID:(NSString *)aReplicantUUID;
+ (NSNumber *)numberOfPendingChangesInLocalSQLiteDatabase:(MCSQLiteContext *)localDB;


- (NSMutableDictionary *)nextServerChangeLog;
- (NSMutableDictionary *)nextClientChangeLog;
- (NSArray *)serverChangeLogsMatchingChangeLog:(NSDictionary *)changeLog;
- (NSArray *)clientChangeLogsMatchingChangeLog:(NSDictionary *)changeLog;

- (NSDate *)startDate;

- (NSNumber *)loadReplicantKey;
- (BOOL)updateRemoteClientDetails:(NSDictionary *)update;
- (BOOL)updateRemoteClientDetailsAfterCreationOrClone;

- (void)finishUp:(int)anExitCode;

- (MCReplicaClientError)prepareLocalDatabaseAfterCreation;
- (void)createReplica;
- (void)performSync;

- (int)run;

/*!
 Note: Conflicts are flushed after the didSyncWithReplicaClient: delegate method.
 */
- (BOOL)hasConflicts;
- (NSArray *)conflicts;
- (void)addConflict:(MCReplicaConflictCandidate *)conflict;
- (void)flushConflicts;

- (void)addRemoteRefreshForEntity:(NSString *)ent primaryKey:(id)primaryKey;

- (void)performCachedValuesTriggerRemoval;

/*!
 Called after the remote notif is posted.
 */
- (void)flushRemoteRefreshes;

/*!
 Subclass responsibility. Default does nothing. Subclass would add conflict objects if necessary and/or remote updates. serverChange or clientChange can be nil. If they are not nil, the change represents the same object.
 */
- (void)determineConflictConditionForServerChange:(NSDictionary *)serverChange clientChange:(NSDictionary *)clientChange;

+ (void)sendSyncNotification:(NSString *)name notificationObject:(id)notifIdentifier info:(NSDictionary *)info;
+ (void)sendSyncFailedNotificationWithResponseCode:(int)resultCode
                                          authCode:(MCSAuthenticationErrorCode)authCode
                                notificationObject:(id)notifIdentifier;

- (void)sendSyncStartedNotification;
- (void)sendSyncFinishedNotification;
- (void)sendSyncFailedNotification:(int)resultCode;
- (void)sendSyncProgressNotificationWithRowsProcessed:(unsigned long long)rows ofTotal:(unsigned long long)total;

@end
