#import "FTPKitConstants.h"
#import "PCNodeStatus.h"
#import "PCOperationStatus.h"
#import <PanicCore/PanicCore.h>
#import <libkern/OSAtomic.h>

/* NOTE: Error codes are found in FTPKitConstants.h */

@class PCNode;
@class PCFileNode;
@class PCOperationResult;
@class PCOperationStatus;
@class PCUUID;


@interface PCFSOperation : NSObject
{
	BOOL				iShouldContinueAfterError;
	NSConditionLock*	iPauseLock;
		
	id					fileManagerDelegate;
	id					interfaceDelegate;
	
@private
	NSConditionLock*	interfaceInteractionLock;
	NSDictionary*		interactionResult;
	
	NSConditionLock*	iChildLock;
@protected
	NSArray*			iNodes;

	NSTimeInterval		iLastStatusUpdate;
	BOOL				iThreadStatusUpdates;
	
	NSMapTable*			iNodeToStatusMap;
	
	NSDictionary*		iOperationUserInfo;
	NSString*			key;
	
	NSScriptCommand*	iScriptCommand;
	
	__weak PCFSOperation*	iParentOperation;
	__weak PCFSOperation*	iChildOperation; // running an operation with a parent operation set, will set this ivar NOTE: to not party on this directly
	BOOL				iConcurrent;
	
	PCOperationStatus*	iStatus;
	PCUUID*				iUUID;
	PCOperationResult*	iResult;
	BOOL				iReportsStatus;
	
	PCThreadFuture*		iThreadFuture;
	OSSpinLock			iNodeStatusLock;
}

- (id)initWithNodes:(NSArray*)nodes;

@property BOOL shouldContinueAfterError; // Operations will prompt after encountering an error, allowing the user the choice to continue. Not all operations support this flag.
@property (getter=isPaused) BOOL paused;

@property (retain) id fileManagerDelegate;
@property (retain) id interfaceDelegate;
@property (nonatomic, readonly, retain) PCUUID* uuid;

@property (assign) BOOL threadStatusUpdates;

@property (retain) NSDictionary* userInfo;
@property (retain) NSScriptCommand* scriptCommand; // this property is for applescript support to track the script command assocated with this operation

@property (readonly) PCOperationStatus *status;
@property (assign) __weak PCFSOperation *childOperation;
@property (assign) __weak PCFSOperation *parentOperation;
@property (copy) NSArray *nodes;
@property (retain) PCOperationResult *result;
@property (assign) BOOL reportsStatus;

@property (assign, getter=isConcurrent) BOOL concurrent; // defaults to NO, only applies to child operations. YES, runs children asynchronously from parent

- (void)start;
- (void)main; // required for subclasses

- (BOOL)runSynchronouslyError:(FTPKitError**)error; //NOTE: unless you have a very specific reason, use runChildOperation::: instead.
- (BOOL)runChildOperation:(PCFSOperation*)op error:(FTPKitError**)error;
- (BOOL)runChildOperation:(PCFSOperation*)op reportStatus:(BOOL)statusFlag error:(FTPKitError**)error;
- (void)setThreadName:(NSString*)name;

- (void)cancel;
- (void)waitIfPaused;

- (BOOL)isReady;
- (BOOL)isExecuting;
- (BOOL)isFinished;
- (BOOL)isCancelled;
- (FTPKitError*)error;
- (void)groupErrorTitle:(NSString**)title message:(NSString**)message; // optional for subclasses

- (void)operationMainWillEnd; // bridges from operation thread to main thread
- (void)operationDidEnd; // main thread callbcak

- (PCNode*)currentNode;
- (void)setCurrentNode:(PCNode*)inNode;
- (void)setCurrentNode:(PCNode*)inNode state:(PCOperationState)inState;
- (void)setCurrentNode:(PCNode*)inNode state:(PCOperationState)inState error:(FTPKitError*)inError;


- (PCNodeStatus*)statusForNode:(PCNode*)aNode;
- (PCNodeStatus*)nodeStatus; // returns the status for the current node

- (PCNodeStatus*)statusForNodeIgnoringChildOperation:(PCNode*)aNode;
- (PCNodeStatus*)statusForNodeIgnoringChildOperation:(PCNode*)aNode create:(BOOL)create;
- (PCNodeStatus*)nodeStatusIgnoringChildOperation;
- (PCNodeStatus*)statusForNode:(PCNode*)aNode ignoringChildOperation:(BOOL)ignoreChild create:(BOOL)create;

- (void)setCaption:(NSString*)caption notifyDelegate:(BOOL)flag;

- (double)progress; // progress of the current node as a fraction of 1.0
- (double)operationProgress; // progress of the entire operation as a fraction of 1.0 or negative if indeterminate

// These must be implemented by subclasses

- (NSString*)captionForNode:(PCNode*)aNode; /*optional, but recommend if using setCurrentNode:*/

- (BOOL)isRemote;

- (PCOperationStatus*)status;

- (NSString*)key;
- (PCFileSystemOperationType)operationType;

- (NSDictionary*)userInfoForNode:(PCNode*)node; // used to build callback dictionaries
- (NSInteger)runAlert:(NSAlert*)anAlert error:(FTPKitError*)error userInfo:(NSObject*)userInfo; // returns NSAlertErrorReturn if the interfaceDelegate does not implement the userfeedback callback
- (FTPKitConflictMode)resolveConflict:(PCNode*)sourceNode destination:(PCNode*)conflictNode conflictMode:(FTPKitConflictMode*)ioMode;

- (BOOL)canCancel;
- (BOOL)canContinueAfterError;	// defaults to NO.... XXX bad API would like to remove if an alternative can be found
- (BOOL)shouldCancelDuplicateOperations; // defaults to NO


/* To obtain user interaction from within an operation, call 
 * obtainResolutionWithContextInfo: which will block the operation's thread and
 * callback to the delegate on the main thread, then wait for the delegate to 
 * respond by calling resolvedOperation:.
 */

- (NSDictionary*)userFeedbackForInfo:(NSDictionary*)contextInfo; // blocking
- (void)userFeedbackResolved:(NSDictionary*)resultInfo;

- (BOOL)isWorthyTrust:(PCTrust*)trust; // blocking
- (void)trust:(PCTrust*)trust wasEvaluated:(PCTrustEvaluation)evaluation;

// These methods set the given values on the main thread and post a notification.

- (void)setState:(PCOperationState)newState onNode:(PCNode*)aNode;
- (void)setState:(PCOperationState)newState error:(FTPKitError*)anError onNode:(PCNode*)aNode;
- (void)setState:(PCOperationState)newState error:(FTPKitError*)anError onNodes:(NSSet*)nodes;

- (PCNodeStatus*)statusForNode:(PCNode*)aNode create:(BOOL)create;
- (void)userFeedbackForInfoOnMainThread:(NSDictionary*)contextInfo;

// Private
// Convenience methods for status changing, performed on worker threads
- (void)notifyDelegateOfStatusChange;
- (void)postDidChangeNodeStatusNotificationIfNeeded:(NSSet*)statuses;
- (void)postOverwriteNotificationForNode:(PCNode*)overwrittenNode replacement:(PCNode*)newNode;

- (void)detachOperationThreadSelector:(SEL)selector withObject:(id)object; // equivalent to +[NSThread detachNewThreadSelector:selector toTarget:self withObject:object] but with autorelease pool

@end

@interface NSObject (PCFSOperationDelegate)

// file manager delegate
- (void)operationDidEnd:(PCFSOperation*)operation;

// interface delegate
- (void)operationDidEnd:(PCFSOperation*)operation error:(FTPKitError*)error;
- (void)operation:(PCFSOperation*)operation requiresUserInteractionWithInfo:(NSDictionary*)contextInfo;
- (void)operation:(PCFSOperation*)operation evaluateTrust:(PCTrust*)trust;
- (void)operation:(PCFSOperation*)operation statusDidChange:(PCOperationStatus*)status; // optionally can be sent on worker thread

// called on both the interface and file manager delegates
- (void)operationWillStart:(PCFSOperation*)operation;

@end

@interface PCFSOperation (Private)

- (BOOL)shouldContinueAfterRunningAlertWithError:(FTPKitError*)error;
- (PCNode*)_currentNode; // nonatomic accessor

@end

@interface PCOperationStatus (NonAtomicOperationAccessors)

@property(nonatomic, readonly) PCNodeStatus *readOnlyNodeStatus;
@property(nonatomic, readonly) PCNode *readOnlyDestinationNode;

@end
