//
//  MCEndpoint.h
//  Sync
//
//  Created by Mark Onyschuk on 15/10/07.
//  Copyright 2007 Marketcircle Inc. All rights reserved.
//

#import <Foundation/Foundation.h>

#define HEADER_TAG		0
#define PAYLOAD_TAG		1


typedef enum {
	kMCNCPayloadEncodingKeyedArchive,
	kMCNCPayloadEncodingPropertyList,
	kMCNCPayloadEncodingJSON
} MCNCPayloadEncoding;


extern NSString *kMCNCProtocolIdentifier;

extern NSString *MCEndpointErrorDomain;


// Meta commands
extern NSString *kMCNCMetaTerminateCommand; // !terminate
extern NSString *kMCNCMetaDoesNotUnderstandCommand; // !does_not_understand
extern NSString *kMCNCMetaResetCommand; // !reset

// Payload keys for !does_not_understand
extern NSString *kMCNCOriginalCommandKey;


typedef enum {
	MCMalformedHeaderError,
	MCMalformedPayloadError,
	MCMessageHandlerError,
	MCMissingCommandNameError,
	MCProtocolStateError
} MCEndpointErrors;

@class MCIOChannel;
@class MCProtocolStateMachine;

@interface MCEndpoint : NSObject {

	MCIOChannel *channel;
	MCProtocolStateMachine *protocolStateMachine;

#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4
	MCNCPayloadEncoding payloadEncoding;
	MCNCPayloadEncoding incomingEncoding;
#endif
	
	long sequenceNumReceive;
	long sequenceNumSend;
	
	NSString	*incomingCommand;
	
	BOOL logMessages;
	BOOL autoReceive;
	BOOL didTerminate;
}

#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
- (BOOL)isChannelOpen;
- (MCIOChannel *)channel;
- (void)setChannel:(MCIOChannel *)aChannel;
- (BOOL)logMessages;
- (void)setLogMessages:(BOOL)flag;
// Auto-receive is necessary to support meta-commands; otherwise, bad things happen
- (BOOL)autoReceive;
- (void)setAutoReceive:(BOOL)flag;
#else
@property(readonly) BOOL isChannelOpen;
@property(retain) MCIOChannel *channel;
@property(readwrite) BOOL logMessages;
@property(readwrite) BOOL autoReceive;
@property(readwrite) MCNCPayloadEncoding payloadEncoding;
#endif

#pragma mark -
#pragma mark Initialization
+ (id)endpoint;

// dedicated initializer
- (id)initWithStateMachine:(MCProtocolStateMachine *)protocolStateMachineInit;

#pragma mark -
#pragma mark Version String
- (NSString *)versionString;
- (BOOL)verifyVersionString:(NSString *)version;

#pragma mark -
#pragma mark Startup/Shutdown
- (BOOL)start:(NSError **)anErrorP;
- (void)stop;

#pragma mark -
#pragma mark Request/Response
- (void)sendMessageWithNoPayload:(NSString *)message;
- (void)sendMessage:(NSDictionary *)aDictionary;

// this method dynamically generates a handler method name of the form "handle_message_name:" which accepts a dictionary
// your custom subclass should include a handler for every message you expect to receive
// @throws NSInternalInconsistencyException if a message handler is not found
- (void)receiveMessage:(NSDictionary *)aDictionary;

- (void)awaitMessage;
// if autoreceive is YES, this method does nothing
- (void)optionallyAwaitMessage;
- (void)terminate;
- (void)abort; // synonym for terminate

#pragma mark -
#pragma mark Meta-commands
/* Meta commands do not start with "meta", they start with "!", which gets stripped
 * meta commands are not part of the normal protocol and are not validated by the protocol state machine
 * therefore, don't use meta-commands as part of your protocol; only use them for what they are intended
 * (to control the protocol state, basically); using them for anything else is a n00b strategy
 */
/*
 * !terminate: closes the channel
 */
- (void)handle_meta_terminate:(NSDictionary *)payload;
/*
 * !does_not_understand: sent back when a command is not understood; payload includes the misunderstood command name
 * this implementation throws an exception
 */
- (void)handle_meta_does_not_understand:(NSDictionary *)payload;
/*
 * !reset: does nothing; subclasses can override
 */
- (void)handle_meta_reset:(NSDictionary *)payload;

#pragma mark -
#pragma mark Derived class notification methods

- (void)handleConnectionError:(NSError *)theError;

#pragma mark -
#pragma mark Cleanup
- (void)finalize;
- (void)dealloc;

// If you use this, or if you use -init, you must implement +protocolName
+ (MCProtocolStateMachine *)defaultStateMachine;
+ (NSString *)protocolIdentifier;

@end

@interface MCEndpoint (MCEndpointSubclass)

+ (NSString *)protocolName;
- (void)willTerminate;

@end

