#import "FTPKitConstants.h"
#import "PCCredentials.h"
#import "PCNodeStatus.h"

@class PCNode;
@class PCRemoteFileNode;
@class PCRemoteFolderNode;
@class PCConnectionThrottle;
@class PCConnectionPool;
@class PCTrust;
@class PCUUID;
@protocol AbstractConnectionTranscriptDelegate;

@interface AbstractConnection : NSObject 
{
	PCCredentials*		iCredentials;
	BOOL				iConnectedOnce;
	id<AbstractConnectionTranscriptDelegate> iTranscriptDelegate;
	
@private
	NSString*			iLoginPath;
	__weak id			iDelegate;
	BOOL				iShouldCancel;
	NSString*			iRemotePath;	// in [[self class] defaultDisplayEncoding]
	PCUUID*				iUUID;
	
	unsigned long long	iBytesTransferred;
	NSDate*				iLastStallDate;
	CFTimeInterval		iStartTransferInterval;
	
	BOOL				iSecure;
	PCTrust*			iTrust;
	
	PCConnectionThrottle* iConnectionThrottle;
	__weak PCConnectionPool*	iConnectionPool;
	
	BOOL iDelegateMonitorsProgress; // conforms to AbstractConnectionProgressMonitoring
}

@property(retain) id transcriptDelegate;
@property(nonatomic, readonly, retain) PCCredentials* credentials;
@property(nonatomic, readonly, copy) NSString* loginPath;
@property(nonatomic, assign) __weak id delegate;
@property BOOL shouldCancel;
@property(copy) NSString* remotePath;
@property(nonatomic, readonly, retain) PCUUID* UUID;
@property(readonly, retain) PCTrust* trust;
@property(retain) PCConnectionThrottle* connectionThrottle;
@property(assign) __weak PCConnectionPool *connectionPool;

@property(readonly) BOOL isSecure; // initialized according to protocol but may change later

/* AbstractConnection is an abstract class: do not call initWithCredentials on AbstractConnection,
   use this constructor instead: */

+ (AbstractConnection*)connectionWithCredentials:(PCCredentials*)credentials;
+ (NSStringEncoding)defaultDisplayEncoding;

// The following methods are provided to simplify the process of determining connection protocol, as a given protocol may come in several wrappers

+ (BOOL)isCloudFiles:(FTPKitProtocol)protocol;
- (BOOL)isCloudFiles;
+ (BOOL)isFTP:(FTPKitProtocol)protocol;
- (BOOL)isFTP;
+ (BOOL)isSFTP:(FTPKitProtocol)protocol;
- (BOOL)isSFTP;
+ (BOOL)isWebDAV:(FTPKitProtocol)protocol;
- (BOOL)isWebDAV;
+ (BOOL)isS3:(FTPKitProtocol)protocol;
- (BOOL)isS3;

// The following methods must be implemented by concrete subclasses:

- (BOOL)changeDir:(NSString*)path error:(FTPKitError**)error;
- (BOOL)changeAttributes:(NSDictionary*)attributes atPath:(NSString*)path error:(FTPKitError**)error;
- (BOOL)contentsAtPath:(NSString*)path nodes:(NSArray**)newNodes error:(FTPKitError**)error;
- (BOOL)connect:(FTPKitError**)error;
- (PCRemoteFolderNode*)createFolderAtPath:(NSString*)path error:(FTPKitError**)error;
- (BOOL)createSymbolicLinkAtPath:(NSString*)path resolvingToPath:(NSString*)toPath error:(FTPKitError**)outError;
- (BOOL)deleteItemAtPath:(NSString*)path isDirectory:(BOOL)isDirectory error:(FTPKitError**)error;
- (void)disconnect;
- (BOOL)downloadFileAtPath:(NSString*)fromPath toFileHandle:(NSFileHandle*)fileHandle byteOffset:(unsigned long long)byteOffset bytesTransferred:(unsigned long long*)outBytesTransferred error:(FTPKitError**)outError; // toPath can be nil
- (BOOL)moveItemFromPath:(NSString*)fromPath isDirectory:(BOOL)isDirectory toPath:(NSString*)toPath error:(FTPKitError**)error;
- (NSString*)pwd:(FTPKitError**)error;
- (BOOL)resolvePath:(NSString*)path resolvedPath:(NSString**)outPath isDirectory:(BOOL*)isDirectory error:(FTPKitError**)outError;
- (BOOL)sendKeepAlive:(FTPKitError**)error;
- (BOOL)supportsUploadByteOffset;
- (BOOL)uploadFileHandle:(NSFileHandle*)fileHandle withFilename:(NSString*)filename toPath:(NSString*)toPath byteOffset:(unsigned long long)byteOffset bytesTransferred:(unsigned long long*)outBytesTransferred error:(FTPKitError**)outError; // fromPath can be nil

- (BOOL)streamBytesWithFilename:(NSString*)filename toPath:(NSString*)toPath handler:(id)handler error:(FTPKitError**)outError;

- (NSNumber*)determineTimeOffsetAtPath:(NSString*)folderPath;

// The following methods are optional for subclasses:

- (id)initWithCredentials:(PCCredentials*)credentials;

- (NSString*)sendRawCommand:(NSString*)command error:(FTPKitError**)outError;
- (NSDictionary*)attributesOfItemAtPath:(NSString*)path isDirectory:(BOOL)isDirectory error:(FTPKitError**)error;
- (NSDictionary*)fileSystemAttributesAtPath:(NSString*)path error:(FTPKitError**)outError;

// end

// convenience methods that don't require a file descriptor
- (BOOL)downloadFileAtPath:(NSString*)fromPath toPath:(NSString*)toPath byteOffset:(unsigned long long)byteOffset bytesTransferred:(unsigned long long*)outBytesTransferred error:(FTPKitError**)error;
- (BOOL)uploadFileAtPath:(NSString*)fromPath toPath:(NSString*)toPath byteOffset:(unsigned long long)byteOffset bytesTransferred:(unsigned long long*)outBytesTransferred error:(FTPKitError**)error;

// the dateWith… methods allow a connection to adjust (or even ignore) the supplied date as is appropriate for the capabilities of the server
- (NSDate*)dateWithDate:(NSDate*)date;
- (NSDate*)dateWithModificationDateForNode:(PCNode*)node supported:(BOOL*)supported;

- (NSString*)address;
- (void)flushTranscript;
- (NSString*)password;
- (unsigned int)port;
- (FTPKitProtocol)protocol;

- (BOOL)setError:(FTPKitError**)error title:(NSString*)title code:(NSInteger)code;
- (BOOL)setError:(FTPKitError**)error title:(NSString*)title message:(NSString*)message code:(NSInteger)code;
- (BOOL)setError:(FTPKitError**)error title:(NSString*)title message:(NSString*)message code:(NSInteger)code userInfo:(NSDictionary*)userInfo;
- (BOOL)setError:(FTPKitError**)error title:(NSString*)title message:(NSString*)message kitError:(FTPKitErrorCode)kitError;
- (BOOL)setError:(FTPKitError**)error title:(NSString*)title message:(NSString*)message kitError:(FTPKitErrorCode)kitError underlyingError:(FTPKitError*)underlyingError;
- (BOOL)setError:(FTPKitError**)error title:(NSString*)title message:(NSString*)message kitError:(FTPKitErrorCode)kitError userInfo:(NSDictionary*)userInfo;

- (NSString*)username;
- (void)appendTranscript:(NSString*)text outgoingText:(BOOL)flag;

- (NSString*)displayStringForRemoteString:(NSString*)string;
- (NSString*)remoteStringForDisplayString:(NSString*)string;

@end


@protocol AbstractConnectionInterfaceDelegate
@required

/* connection:runAlert:
 * This callback tells the delegate to run an alert dialog and wait for a reply
 */

- (NSInteger)connection:(AbstractConnection*)connection runAlert:(NSAlert*)alert;

- (BOOL)connection:(AbstractConnection*)connection isWorthyTrust:(PCTrust*)trust;

@end


@protocol AbstractConnectionTranscriptDelegate
@required

/* connectionUUID:appendTranscript:isOutgoing:
 * This callback feeds the receiver logging information for the connection as it
 * happens.  The BOOL "isOutgoing" indicates that this communication was sent
 * to the server rather than received from the server.
 */

- (void)connectionUUID:(PCUUID*)UUID appendTranscript:(NSString*)message isOutgoing:(BOOL)isOutgoing;


/* connectionUUIDFlushTranscript:
 * This callback is used to indicate that the transcript should be flushed.
 */

- (void)connectionUUIDFlushTranscript:(PCUUID*)UUID;

@end


@protocol AbstractConnectionProgressMonitoring
@required

/* connection:transferredBytes:throughput:stalled:
 * This callback informs the receiver of the file transfer progress.
 * Bytes are the total number of bytes transferred so far in the current file.
 * Throughput is the transfer rate in bytes per second and stalled indicates if the connection is currently stalled.
 */
- (void)connection:(AbstractConnection*)connection transferredBytes:(long long)bytes throughput:(double)kbytesPerSecond stalled:(BOOL)stalled;

@end


@protocol AbstractConnectionStreaming
@required

- (NSUInteger)connection:(AbstractConnection*)connection streamBytes:(void*)bytes length:(NSUInteger)length;

@end


extern NSStringEncoding FTPKitDefaultStringEncodingForProtocol(FTPKitProtocol protocol);



@interface PCThrottleInfo : NSObject
{
	double			iCurrentKBytes;
	double			iMaxKBytes;
	NSTimeInterval	iLastSleepInterval;
	BOOL			iIsUpload;
}

@property double currentKBytes;
@property double maxKBytes;
@property NSTimeInterval lastSleepInterval;
@property BOOL isUpload;

- (void)reset;

@end


@interface PCConnectionThrottle : NSObject
{
	PCThrottleInfo	*iDownloadInfo;
	PCThrottleInfo	*iUploadInfo;
}

@property(retain) PCThrottleInfo *downloadInfo;
@property(retain) PCThrottleInfo *uploadInfo;

- (void)reset;

@end


@interface PCCredentials (AbstractConnection)

- (NSString*)remoteStringForUnicodeString:(NSString*)string;
- (NSString*)unicodeStringForRemoteString:(NSString*)string;

@end


@interface NSString (PCLossyCString)

- (const char*)pc_lossyCStringUsingEncoding:(NSStringEncoding)encoding;

@end
