#pragma once

#include "FlowSshCppUtils.h"


//
// Usage Summary for FlowSshCpp 
// ----------------------------
//
// Reference counted objects:
//  - Main objects:    Keypair, PublicKey, Client, ClientSessionChannel, ClientSftpChannel
//  - Handler objects: ErrorHandler, ProgressHandler, ReceiveHandler, RealPathHandler, 
//                     StatHandler, SftpHandler, ListHandler, TransferHandler
//
// 1) Always create them on the heap (dynamic allocation).
// 
// 2) Never delete them.
//    They provide AddRef and Release calls similar to those found in COM objects.
//    The FlowSshCpp wrapper object will be destroyed when its reference count reaches 0.
//    The internal FlowSshC object being wrapped may continue to live, and will be destroyed
//    when FlowSshC releases it as well.
//
// 3) Use our convenient RefPtr. Example:
//        RefPtr<Client> client(new Client);
//
//    RefPtr manages the necessary AddRef and Release calls of the wrapped object.
//    On occasions where you cannot use RefPtr be sure to call AddRef and Release as appropriate.
//
//
// Handler: FlowSshC has so called handler functions. Essentially they are callback-functions
// which are usually called by internal FlowSshC worker threads. FlowSshCpp wraps 
// these callbacks through a higher level abstraction and provides them to you through 
// classes and convenient virtual methods which you can override when needed.
//


namespace FlowSshCpp
{


// ErrorHandler
//
// OnExceptionInHandler: An uncatched error/exception occurred in one of you
// handlers. Never throw exceptions from within OnExceptionInHandler itself.
// Parameters: fatal == true:
//		error caught by FlowSshC's structured-exception-handling; memory leaks might
//		occur due to undestroyed C++ objects created on the stack of the handler
//		Terminate the application in response to fatal errors. In fact for some errors 
//		the core library (FlowSshC) terminates the process automatically after this event.
//
// By default every FlowSshC call can throw an Exception unless you register your
// own error handler through FlowSshC_Initialize. In any case a custom error handler
// MUST also throw some kind of exceptions for FlowSshC_ErrorFlags::InCall errors.

class ErrorHandler : public RefCountable, public NoCopy
{
protected:
	// This object must be created on the heap.
	virtual ~ErrorHandler() {}

public:
	virtual void OnExceptionInHandler(bool fatal, wchar_t const* desc) = 0;
};



// Initializer
//
// Before using any FlowSshCpp functionality create a SINGLE instance of Initializer (i.e. 
// at the beginning of your main function). Don't create a global instance of this class.

class InitializerExistsException : public Exception
{ 
public:
	InitializerExistsException () : Exception(L"An Initializer already exists.") {}
};


class Initializer : public NoCopy
{
public:
	Initializer(RefPtr<ErrorHandler> errHandler);
	~Initializer();

	static FlowSshC_Version GetVersion();
	void SetActCode(wchar_t const* actCodeHex);
	void Shutdown();

private:
	static void __cdecl FlowSshC(void* thisPtr, unsigned int flags, wchar_t const* desc);
	friend void ExceptionInHandler(std::exception const& e, wchar_t const* handlerName, wchar_t const* defErrDesc);
	friend class DefaultErrorHandler;
	
	static LONG m_count;
	static RefPtr<ErrorHandler> m_errHandler;
	static FlowSshC_ErrorHandler m_extHandler;

public:
	// For internal FlowSshNet usage only!
	static void RegisterExtErrHandler(FlowSshC_ErrorHandler extHandler);
};



// ProgressHandler

class ProgressHandler
	: public HandlerBase<ProgressHandler, false>
{
protected:
	// This object must be created on the heap.
	virtual ~ProgressHandler() {}

public:
	ProgressHandler();

	bool Success() const;
	unsigned int GetTaskState() const;
	unsigned int GetTaskSpecificStep() const;
	std::wstring GetAuxInfo() const;

	std::wstring DescribeConnectError() const;
	std::wstring DescribeSftpChannelOpenError() const;

	static std::wstring DescribeConnectError(unsigned int step, wchar_t const* auxInfo);
	static std::wstring DescribeSftpChannelOpenError(unsigned int step, wchar_t const* auxInfo);

protected:
	virtual void OnStart();

	// Callback event handlers
	// Note: handlers are usually called from a worker thread (created in FlowSshC.dll)
	virtual void OnDone() {}
	virtual void OnProgress(unsigned int /*taskSpecificStep*/) {}
	virtual void OnSuccess() {}
	virtual void OnError(unsigned int /*taskSpecificStep*/, std::wstring const& /*auxInfo*/){}

private:
	static void __cdecl FlowSshC(void* thisPtr, unsigned int taskState, unsigned int taskSpecificStep, wchar_t const* auxInfo);

	// members
	mutable CriticalSection m_cs;
	bool m_bSuccess;
	unsigned int m_taskState; 
	unsigned int m_taskSpecificStep;
	std::wstring m_auxInfo;

	// friends
	friend class Client;
	friend class ClientSessionChannel;
	friend class ClientSftpChannel;
};



// Keypair

class KeypairLoadException : public Exception
{ 
public:
	KeypairLoadException () : Exception(L"Error loading keypair") {}
};

class KeypairExportException : public Exception
{ 
public:
	KeypairExportException () : Exception(L"Error exporting keypair") {}
};


class Keypair : public MainBase
{
protected:
	// This object must be created on the heap.
	virtual ~Keypair();

public:
	Keypair(wchar_t const* alg, unsigned int bitCount);
	Keypair(Data const& data, wchar_t const* passphrase);
	Keypair(FlowSshC_Keypair* flowSshC);
	
	void SetPassphrase(wchar_t const* passphrase);
	Data GetBitviseData() const;
	Data GetOpenSshData() const;
	Data GetPuttyData() const;

	FlowSshC_Keypair* GetFlowSshC() const { return m_flowSshC; }

private:
	// members
	FlowSshC_Keypair* m_flowSshC;
};



// PublicKey

class PublicKeyLoadException : public Exception
{
public: 
	PublicKeyLoadException () : Exception(L"Error loading public key") {}
};


class PublicKey : public MainBase
{
protected:
	// This object must be created on the heap.
	virtual ~PublicKey();

public:
	PublicKey(RefPtrConst<Keypair> const& keypair);	
	PublicKey(Data const& data);
	PublicKey(FlowSshC_PublicKey* flowSshC);

	unsigned int	GetBitCount() const;
	unsigned int	GetEffectiveSecurity() const;
	bool            IsEqual(PublicKey const* other) const;
	std::wstring	GetAlg() const;
	std::wstring	GetMd5() const;
	std::wstring	GetBubbleBabble() const;
	std::wstring	GetSha256() const;
	Data			GetSsh2Data() const;
	Data			GetOpenSshData() const;

	FlowSshC_PublicKey* GetFlowSshC() const { return m_flowSshC; }

private:
	// members
	FlowSshC_PublicKey* m_flowSshC;
};



// Algorithms

struct KeyExchangeAlgs : public FlowSshC_KeyExchangeAlgs
{
	KeyExchangeAlgs() 
	{
		m_mlkem1024nistp384 = 1;
		m_mlkem768x25519 = 1;

		m_curve25519 = 1;
		m_ecdhSecp256k1 = 1;
		m_ecdhNistp512 = 1;
		m_ecdhNistp384 = 1;
		m_ecdhNistp256 = 1;

		m_gexBitsMin = 0;
		m_gexBitsOpt = 0;
		m_gexBitsMax = 0;

		m_dhG16Sha512 = 1;
		m_dhG15Sha512 = 1;
		m_dhG14Sha256 = 1;
		m_dhG14Sha1 = 0;
		m_dhG1Sha1 = 0; 
		m_dhGexSha256 = 1;
		m_dhGexSha1 = 0;
	}
};

struct HostKeyAlgs : public FlowSshC_HostKeyAlgs
{
	HostKeyAlgs()
	{
		m_rsaSha2_512 = 1;
		m_rsaSha2_256 = 1;
		m_ed25519 = 1;
		m_ecdsaSecp256k1 = 1;
		m_ecdsaNistp521 = 1;
		m_ecdsaNistp384 = 1;
		m_ecdsaNistp256 = 1;
		m_sshRsa = 0;
		m_sshDss = 0;
	}
};

struct EncryptionAlgs : public FlowSshC_EncryptionAlgs
{
	EncryptionAlgs() 
	{ 
		m_aes256Gcm = m_aes128Gcm = m_aes256Ctr = m_aes192Ctr = m_aes128Ctr = 1;
		m_chacha20Poly1305 = m_tripleDesCtr = m_aes256Cbc = m_aes192Cbc = m_aes128Cbc = m_tripleDesCbc = 0;
	}
};

struct MacAlgs : public FlowSshC_MacAlgs
{
	MacAlgs() 
	{ 
		m_hmacSha2_256_etm = m_hmacSha2_512_etm = 0;
		m_hmacSha2_256     = m_hmacSha2_512     = 1;
		m_hmacSha1_etm     = m_hmacSha1         = 0;
	}
};

struct CompressionAlgs : public FlowSshC_CompressionAlgs
{
	CompressionAlgs() { m_zlib = 0; m_none = 1; }
};



// Options

struct Options : public FlowSshC_Options, public FlowSshC_Options2
{
	bool m_requireStrictKex;

	Options()
		: m_requireStrictKex(false)
	{ 
		FlowSshC_Options_Initialize(this);
		FlowSshC_Options2_Initialize(this);
		m_requireStrictKex = false;
	}
};



// ForwardingRule

struct ForwardingRuleRef
{
	// S2C: When forwarding from a Unix socket, m_listInterface must start with '/'
	// and m_listPort must be set to FLOWSSHC_PORT_UNIXSOCKET.
	bool m_clientToServer;
	std::wstring m_listInterface;
	unsigned int m_listPort;
};

struct ForwardingRule : public ForwardingRuleRef
{
	// C2S: When forwarding to a Unix socket, m_destHost must start with '/'
	// and m_destPort must be set to FLOWSSHC_PORT_UNIXSOCKET.
	std::wstring m_destHost;
	unsigned int m_destPort;
};

struct ForwardingRuleExt : public ForwardingRule
{
	std::wstring m_connectInterface;
};



// ProxyForwarding

struct ProxyForwarding
{
	std::wstring m_listInterface;
	unsigned int m_listPort;
	std::wstring m_bindPublicAddressIP4;
	std::wstring m_bindPublicAddressIP6;
	std::wstring m_bindListInterfaceIP4;
	std::wstring m_bindListInterfaceIP6;
};



// ForwardingErr

struct ForwardingErr
{
	unsigned int m_errCode;
	std::wstring m_auxInfo;
	std::wstring m_desc;

	ForwardingErr() { Reset(); }
	void Reset();
	ForwardingErr& operator= (FlowSshC_ForwardingErr const* x);
};



// ForwardingHandler

class ForwardingHandler
	: public HandlerBase<ForwardingHandler, false>
{
protected:
	// This object must be created on the heap.
	virtual ~ForwardingHandler() {}

public:
	ForwardingHandler();

	bool Success() const;
	unsigned int GetListPort() const;
	ForwardingErr GetError() const;

protected:
	virtual void OnStart();

	// Callback event handlers
	// Note: handlers are usually called from a worker thread (created in FlowSshC.dll)
	virtual void OnDone() {}
	virtual void OnSuccess(unsigned int /*listPort*/) {}
	virtual void OnError(ForwardingErr const& /*error*/) {}

private:
	static void __cdecl FlowSshC(void* thisPtr, unsigned int listPort, FlowSshC_ForwardingErr const* error);

	// members
	mutable CriticalSection m_cs;
	bool m_bSuccess;
	unsigned int m_listPort;
	ForwardingErr m_error;

	// friends
	friend class Client;
};



// ForwardingLog

struct ForwardingLog : public RefCountable
{
	enum Event
	{
		C2SOpenSent				= 1,	// ForwardingLog_C2S
		C2SOpenFailed			= 2,	// ForwardingLog_C2SOpenFailed
		C2SOpened				= 3,	// ForwardingLog_C2S
		C2SClosed				= 4,	// ForwardingLog_C2SClosed

		S2COpenIgnored			= 11,	// ForwardingLog_S2C
		S2COpenReceived			= 12,	// ForwardingLog_S2C
		S2COpenFailed			= 13,	// ForwardingLog_S2COpenFailed
		S2COpened				= 14,	// ForwardingLog_S2C
		S2CClosed				= 15,	// ForwardingLog_S2CClosed

		ServerSideC2SAdded		= 31,	// ForwardingLog_ServerSideC2S
		ServerSideC2SAddFailed	= 32,	// ForwardingLog_ServerSideC2SAddFailed
		ServerSideC2SCanceled	= 33,	// ForwardingLog_ServerSideC2S

		ProxyAccepted			= 51,	// ForwardingLog_Proxy
		ProxyDecodeFailed		= 52,	// ForwardingLog_ProxyDecodeFailed
		ProxyConnectStarted		= 53,	// ForwardingLog_ProxyStarted
		ProxyBindFailed			= 54,	// ForwardingLog_ProxyBindFailed
		ProxyBindStarted		= 55,	// ForwardingLog_ProxyBindStarted
		ProxyBindAborted		= 56	// ForwardingLog_ProxyBindAborted
	};

	unsigned int m_event; // Event
	std::wstring m_desc;
	
	static RefPtr<ForwardingLog> Create(FlowSshC_ForwardingLog const* x);

	ForwardingLog(FlowSshC_ForwardingLog const* x);
};

struct ForwardingLog_X2Y : public ForwardingLog
{
	enum Type
	{
		ProxySocks4			= 1,
		ProxySocks5			= 2,
		ProxyHttpConnect	= 3,
		ClientSide			= 9,
		ServerSide			= 10
	};

	unsigned int m_type; // Type
	std::wstring m_srvSideRuleDesc;
	std::wstring m_listInterface;
	unsigned int m_listPort;
	std::wstring m_origAddress;
	unsigned int m_origPort;
	std::wstring m_destAddress;
	unsigned int m_destPort;

	ForwardingLog_X2Y(FlowSshC_ForwardingLog_C2S const* x);
	ForwardingLog_X2Y(FlowSshC_ForwardingLog_S2C const* x);
};

struct ForwardingLog_X2YOpenFailed : public ForwardingLog_X2Y
{
	std::wstring m_auxInfo;

	ForwardingLog_X2YOpenFailed(FlowSshC_ForwardingLog_C2SOpenFailed const* x);
	ForwardingLog_X2YOpenFailed(FlowSshC_ForwardingLog_S2COpenFailed const* x);
};

struct ForwardingLog_X2YClosed : public  ForwardingLog_X2Y
{
	unsigned __int64 m_bytesSent;
	unsigned __int64 m_bytesReceived;

	ForwardingLog_X2YClosed(FlowSshC_ForwardingLog_C2SClosed const* x);
	ForwardingLog_X2YClosed(FlowSshC_ForwardingLog_S2CClosed const* x);
};

typedef ForwardingLog_X2Y			ForwardingLog_C2S;
typedef ForwardingLog_X2YOpenFailed	ForwardingLog_C2SOpenFailed;
typedef ForwardingLog_X2YClosed		ForwardingLog_C2SClosed;

typedef ForwardingLog_X2Y			ForwardingLog_S2C;
typedef ForwardingLog_X2YOpenFailed	ForwardingLog_S2COpenFailed;
typedef ForwardingLog_X2YClosed		ForwardingLog_S2CClosed;

struct ForwardingLog_ServerSideC2S : public ForwardingLog
{
	std::wstring m_listInterface;
	unsigned int m_listPort;
	std::wstring m_ruleDesc;

	ForwardingLog_ServerSideC2S(FlowSshC_ForwardingLog_ServerSideC2S const* x);
};

struct ForwardingLog_ServerSideC2SAddFailed : public ForwardingLog_ServerSideC2S
{
	enum ErrCode
	{
		GeneralError		= 2,
		AddressInUse		= 4,
		NoInvitation		= 100
	};

	unsigned int m_errCode; // ErrCode
	std::wstring m_auxInfo;

	ForwardingLog_ServerSideC2SAddFailed(FlowSshC_ForwardingLog_ServerSideC2SAddFailed const* x);
};
	
struct ForwardingLog_Proxy : public ForwardingLog
{
	std::wstring m_proxyListInterface;
	unsigned int m_proxyListPort;
	std::wstring m_proxyOrigAddress;
	unsigned int m_proxyOrigPort;

	ForwardingLog_Proxy(FlowSshC_ForwardingLog_Proxy const* x);
};

struct ForwardingLog_ProxyDecodeFailed : public ForwardingLog_Proxy
{
	enum ErrCode
	{
		ConnectionEof		= 0,
		ConnectionError		= 1,
		DecodeError			= 2
	};

	unsigned int m_errCode; // ErrCode
	std::wstring m_auxInfo;

	ForwardingLog_ProxyDecodeFailed(FlowSshC_ForwardingLog_ProxyDecodeFailed const* x);
};

struct ForwardingLog_ProxyStarted : public ForwardingLog_Proxy
{
	enum ProxyType
	{
		ProxySocks4			= 1,
		ProxySocks5			= 2,
		ProxyHttpConnect	= 3
	};

	unsigned int m_proxyType; // ProxyType
	std::wstring m_proxyReqAddress;
	unsigned int m_proxyReqPort;

	ForwardingLog_ProxyStarted(FlowSshC_ForwardingLog_ProxyStarted const* x);
};

struct ForwardingLog_ProxyBindFailed : public ForwardingLog_ProxyStarted
{
	enum ErrCode
	{
		NoServerAddress			= 0,
		NoIp4ServerAddress		= 1,
		AddressResolveError		= 2,
		NoIp4AddressResolved	= 3,
		NoIpAddressTypeMatch	= 4,
		ProxyS2CAddFailed		= 5
	};

	unsigned int m_errCode; // ErrCode
	std::wstring m_auxInfo;

	ForwardingLog_ProxyBindFailed(FlowSshC_ForwardingLog_ProxyBindFailed const* x);
};

struct ForwardingLog_ProxyBindStarted : public ForwardingLog_ProxyStarted
{
	std::wstring m_bindPublicAddress;
	unsigned int m_bindPublicPort;
	std::wstring m_bindListInterface;
	unsigned int m_bindListPort;

	ForwardingLog_ProxyBindStarted(FlowSshC_ForwardingLog_ProxyBindStarted const* x);
};

struct ForwardingLog_ProxyBindAborted : public ForwardingLog_ProxyBindStarted
{
	enum AbrtCode
	{
		ConnectionEof		= 0,
		ConnectionError		= 1,
		AcceptTimeout		= 2
	};
		
	unsigned int m_abrtCode; // AbrtCode
	std::wstring m_auxInfo;

	ForwardingLog_ProxyBindAborted(FlowSshC_ForwardingLog_ProxyBindAborted const* x);
};



// Client

class Client : public MainBase
{
protected:
	// This object must be created on the heap.
	virtual ~Client() {}
	virtual void OnDestroy() const;

public:
	Client();
	
	void SetAppName(wchar_t const* appNameAndVersion);
	void SetDebugFile(wchar_t const* debugFile, unsigned int debugEventMask);

	void SetProxyType(unsigned int type);
	void SetProxyHost(wchar_t const* host);
	void SetProxyPort(unsigned int port);
	void SetProxyUserName(wchar_t const* userName);
	void SetProxyPassword(wchar_t const* password);
	void SetProxyOptions(bool resolveLocally);
	
	void SetHost(wchar_t const* host);
	void SetPort(unsigned int port) { FlowSshC_Client_SetPort(m_flowSshC, port); } // **
	void SetObfuscation(bool enable, wchar_t const* keyword);
	void SetUserName(wchar_t const* userName);
	void SetPassword(wchar_t const* password);
	void SetKeypair(RefPtrConst<Keypair> const& keypair);
	void SetSessionId(wchar_t const* clientSessionId, unsigned int timeoutMs);

	void SetKeyExchangeAlgs(KeyExchangeAlgs const& algs);
	void SetHostKeyAlgs(HostKeyAlgs const& algs);
	void SetEncryptionAlgs(EncryptionAlgs const& algs);
	void SetMacAlgs(MacAlgs const& algs);
	void SetCompressionAlgs(CompressionAlgs const& algs);
	void SetOptions(Options const& opts);
	
	void SetSocketProvider(unsigned int socketProvider);
	void SetPreferredIPVersion(unsigned int version);
	void SetConnectInterface(wchar_t const* interfaceList);
	
	void Connect(RefPtr<ProgressHandler> const& progress);
	void Disconnect(RefPtr<ProgressHandler> const& progress);

	void AddForwarding(ForwardingRule const& rule, RefPtr<ForwardingHandler> const& response);
	void AddForwarding(ForwardingRuleExt const& rule, RefPtr<ForwardingHandler> const& response);
	void CancelForwarding(ForwardingRuleRef const& ruleRef, RefPtr<ForwardingHandler> const& response);
	void InviteForwardings(bool clientToServer, RefPtr<ForwardingHandler> const& response);
	void EnableProxyForwarding(ProxyForwarding const& settings, RefPtr<ForwardingHandler> const& response);
	void DisableProxyForwarding(RefPtr<ForwardingHandler> const& response);
		
	FlowSshC_Client* GetFlowSshC() const { return m_flowSshC; }

	// ** inline implementation prevents linker issues caused by
	//    "#define SetPort  SetPortW" in winspool.h

public:
	class FurtherAuth : public NoCopy
	{
	public:
		FurtherAuth(FlowSshC_FurtherAuth* flowSshC);
		void SetUserName(wchar_t const* userName);
		void SetPassword(wchar_t const* password);
		void SetKeypair(RefPtrConst<Keypair> const& keypair);

		bool HavePartialSuccess() const;
		bool IsPasswordRemaining() const;
		bool IsPublicKeyRemaining() const;
	private:
		FlowSshC_FurtherAuth* m_flowSshC;
	};

	class PasswordChange : public NoCopy
	{
	public:
		PasswordChange(FlowSshC_PasswordChange* flowSshC);
		std::wstring	GetPrompt() const;
		void			SetNewPassword(wchar_t const* password);
	private:
		FlowSshC_PasswordChange* m_flowSshC;
	};

protected:
	// Callback event handlers
	// Note: handlers are usually called from a worker thread (created in FlowSshC.dll)

	virtual void OnSshVersion(std::wstring const& /*version*/) {}

	// Return false to reject host key and disconnect.
	virtual bool OnHostKey(RefPtr<PublicKey> /*publicKey*/) { return false; }
	
	virtual void OnKexDone(unsigned __int64 /*kexNr*/, bool /*incoming*/, std::wstring const& /*kexAlg*/, std::wstring const& /*encAlg*/, std::wstring const& /*macAlg*/, std::wstring const& /*cmprAlg*/) {}
	
	// Return false to stop user authentication and disconnect.
	virtual bool OnFurtherAuth(FurtherAuth& /*furtherAuth*/) { return false; }
	// Return false to abort password change and continue with user authentication.
	virtual bool OnPasswordChange(PasswordChange& /*passwordChange*/) { return false; }

	virtual void OnBanner(std::wstring const& /*banner*/) {}

	virtual void OnSessionIdReply(unsigned int /*code*/) {}

	virtual void OnServerNotice(unsigned int /*severity*/, std::wstring const& /*facility*/, std::wstring const& /*status*/, std::wstring const& /*description*/) {}

	virtual bool OnHostKeySync(RefPtr<PublicKey> /*publicKey*/, bool /*keyVerified*/) { return false; }

	virtual void OnForwardingLog(RefPtrConst<ForwardingLog> const& /*log*/) {}

	virtual void OnDisconnect(unsigned int /*reason*/, std::wstring const& /*desc*/) {}

private:
	static void __cdecl OnSshVersion(void* thisPtr, wchar_t const* version);
	static bool __cdecl OnHostKey(void* thisPtr, FlowSshC_PublicKey* publicKey);
	static void __cdecl OnKexDone(void* thisPtr, unsigned __int64 kexNr, bool incoming, wchar_t const* kexAlg, wchar_t const* encAlg, wchar_t const* macAlg, wchar_t const* cmprAlg);
	static bool __cdecl OnUserAuth(void* thisPtr, FlowSshC_FurtherAuth* furtherAuth, FlowSshC_PasswordChange* passwordChange);
	static void __cdecl OnBanner(void* thisPtr, wchar_t const* banner);
	static void __cdecl OnSessionIdReply(void* thisPtr, unsigned int code);
	static void __cdecl OnServerNotice(void* thisPtr, unsigned int severity, wchar_t const* facility, wchar_t const* status, wchar_t const* description);
	static bool __cdecl OnHostKeySync(void* thisPtr, FlowSshC_PublicKey* publicKey, bool keyVerified);
	static void __cdecl OnForwardingLog(void* thisPtr, FlowSshC_ForwardingLog const* log);
	static void __cdecl OnDisconnect(void* thisPtr, unsigned int reason, wchar_t const* desc);
	
	// members
	mutable FlowSshC_Client* m_flowSshC;
};



// ReceiveHandler

class ReceiveHandler
	: public HandlerBase<ReceiveHandler, true>
{
protected:
	// This object must be created on the heap.
	virtual ~ReceiveHandler() {}

public:
	ReceiveHandler();

	bool Success() const;
	bool StdErr()  const;
	bool Eof()     const;
	// Don't use GetDataPtr or GetDataSize before OnDone is called
	unsigned char const* GetDataPtr()  const;
	unsigned int		 GetDataSize() const;

protected:
	virtual void OnStart();

	// Callback event handlers
	// Note: handlers are usually called from a worker thread (created in FlowSshC.dll)
	virtual void OnDone() {}
	virtual void OnReceive(bool /*stdErr*/, unsigned char const* /*dataPtr*/, unsigned int /*dataSize*/, bool /*eof*/) {}
	virtual void OnError() {}	// Called if the associated ClientSessionChannel is or gets closed	

private:
	static void __cdecl FlowSshC(void* thisPtr, bool stdErr, unsigned char const* dataPtr, unsigned int dataSize, bool eof);

	// members
	mutable CriticalSection m_cs;
	bool m_bSuccess;
	bool m_bStdErr;
	bool m_bEof;
	Data m_data;

	// friends
	friend class ClientSessionChannel;
};



// ClientSessionChannel

class ClientSessionChannel : public MainBase
{
protected:
	// This object must be created on the heap.
	virtual ~ClientSessionChannel() {}
	virtual void OnDestroy() const;

public:
	ClientSessionChannel(RefPtrConst<Client> const& client);
	RefPtr<Client> GetClient() { return CastAsNonConst(m_client); }

	void OpenRequest(RefPtr<ProgressHandler> const& progress);
	void PtyRequest(wchar_t const* term, unsigned int widthCols, unsigned int heightRows, RefPtr<ProgressHandler> const& progress);
	void ExecRequest(wchar_t const* command, RefPtr<ProgressHandler> const& progress);
	void ShellRequest(RefPtr<ProgressHandler> const& progress);
	void Receive(unsigned int maxBytes, RefPtr<ReceiveHandler> const& receive);
	void Receive(RefPtr<ReceiveHandler> const& receive, unsigned int maxBytes = 32*1024) { Receive(maxBytes, receive); }
	void Send(Data const& data, bool eof, RefPtr<ProgressHandler> const& progress);
	void Signal(wchar_t const* signalName, RefPtr<ProgressHandler> const& progress);
	void Close(RefPtr<ProgressHandler> const& progress);

	struct ExitSignal
	{
		std::wstring m_name; 
		bool m_coreDumped;
		std::wstring m_errMsg;
	};

protected:
	// Callback event handlers
	// Note: handlers are usually called from a worker thread (created in FlowSshC.dll)
	virtual void OnExitStatus(FlowSshC_ExitStatus const& /*status*/) {}
	virtual void OnExitSignal(ExitSignal const& /*signal*/) {}
	virtual void OnChannelClose() {}

private:
	static void __cdecl OnExit(void* thisPtr, FlowSshC_ExitStatus const* status, FlowSshC_ExitSignal const* signal);
	static void __cdecl OnChannelClose(void* thisPtr);

	// members
	mutable FlowSshC_ClientSessionChannel* m_flowSshC;
	RefPtrConst<Client> m_client; // Channel keeps the associated client alive.
};



// FileAttrs

struct FileAttrs
{
	unsigned int m_validAttrFlags;					// FlowSshC_AttrFlags mask of file attributes available
	unsigned char m_type;							// Always present
	unsigned __int64 m_size;						// FlowSshC_Attrs::Size
	unsigned int m_uid; unsigned int m_gid;			// FlowSshC_AttrFlags::UidGid
	unsigned int m_permissions;						// FlowSshC_AttrFlags::Permissions
	unsigned __int64 m_accessTime;					// FlowSshC_AttrFlags::AccessTime
	unsigned int m_accessTimeNs;					// FlowSshC_AttrFlags::AccessTime && FlowSshC_AttrFlags::Subseconds
	unsigned __int64 m_createTime;					// FlowSshC_AttrFlags::CreateTime
	unsigned int m_createTimeNs;					// FlowSshC_AttrFlags::CreateTime && FlowSshC_AttrFlags::Subseconds
	unsigned __int64 m_modifyTime;					// FlowSshC_AttrFlags::ModifyTime
	unsigned int m_modifyTimeNs;					// FlowSshC_AttrFlags::ModifyTime && FlowSshC_AttrFlags::Subseconds
	std::wstring m_owner; std::wstring m_group;		// FlowSshC_AttrFlags::OwnerGroup
	unsigned __int64 m_allocSize;					// FlowSshC_AttrFlags::AllocSize
	unsigned char m_textHint;						// FlowSshC_AttrFlags::TextHint

	FileAttrs() { Reset(); }
	void Reset();
	FileAttrs& operator= (FlowSshC_FileAttrs const* x);
};


// FileInfo

struct FileInfo
{
	std::wstring m_name;
	FileAttrs m_attrs;

	FileInfo() {}
	FileInfo(FlowSshC_FileInfo const* x);
	FileInfo& operator= (FlowSshC_FileInfo const* x);
};


// SftpErr

struct SftpErr
{
	unsigned int m_errCode;
	std::wstring m_errMsg;

	SftpErr() { Reset(); }
	void Reset();
	SftpErr& operator= (FlowSshC_SftpErr const* x);

	std::wstring Describe() const;
};


// ListErr

struct ListErr
{
	unsigned int m_listOp;
	unsigned int m_errCode; 
	std::wstring m_errMsg;

	ListErr() { Reset(); }
	void Reset();
	ListErr& operator= (FlowSshC_ListErr const* x);

	std::wstring Describe() const;
};


// TransferErr

struct TransferErr
{
	unsigned int m_transferOp;
	unsigned int m_errCode; 
	std::wstring m_errMsg;

	TransferErr() { Reset(); }
	void Reset();
	TransferErr& operator= (FlowSshC_TransferErr const* x);

	std::wstring Describe() const;
};



// RealPathHandler

class RealPathHandler
	: public HandlerBase<RealPathHandler, true>
{
protected:
	// This object must be created on the heap.
	virtual ~RealPathHandler() {}

public:
	RealPathHandler();

	bool Success() const;
	std::wstring	GetRealPath() const;
	SftpErr			GetError() const;	

protected:
	virtual void OnStart();

	// Callback event handlers
	// Note: handlers are usually called from a worker thread (created in FlowSshC.dll)
	virtual void OnDone() {}
	virtual void OnRealPath(std::wstring const& /*realPath*/) {}
	virtual void OnError(SftpErr const& /*error*/) {}

private:
	static void __cdecl FlowSshC(void* thisPtr, wchar_t const* realPath, FlowSshC_SftpErr const* error);

	// members
	mutable CriticalSection m_cs;
	bool m_bSuccess;
	std::wstring m_sRealPath;
	SftpErr m_error;

	// friends
	friend class ClientSftpChannel;
};



// StatHandler

class StatHandler
	: public HandlerBase<StatHandler, true>
{
protected:
	// This object must be created on the heap.
	virtual ~StatHandler() {}

public:
	StatHandler();

	bool Success() const;
	FileAttrs	GetFileAttrs() const;
	SftpErr		GetError() const;

protected:
	virtual void OnStart();

	// Callback event handlers
	// Note: handlers are usually called from a worker thread (created in FlowSshC.dll)
	virtual void OnDone() {}
	virtual void OnStat(FileAttrs const& /*fileAttrs*/) {}
	virtual void OnError(SftpErr const& /*error*/) {}

private:
	static void __cdecl FlowSshC(void* thisPtr, FlowSshC_FileAttrs const* fileAttrs, FlowSshC_SftpErr const* error);

	// members
	mutable CriticalSection m_cs;
	bool m_bSuccess;
	FileAttrs m_fileAttrs;
	SftpErr m_error;

	// friends
	friend class ClientSftpChannel;
};



// SftpHandler

class SftpHandler
	: public HandlerBase<SftpHandler, false>
{
protected:
	// This object must be created on the heap.
	virtual ~SftpHandler() {}

public:
	SftpHandler();

	bool Success() const;	
	SftpErr GetError() const;

protected:
	virtual void OnStart();

	// Callback event handlers
	// Note: handlers are usually called from a worker thread (created in FlowSshC.dll)
	virtual void OnDone() {}
	virtual void OnSuccess() {}
	virtual void OnError(SftpErr const& /*error*/) {}

private:
	static void __cdecl FlowSshC(void* thisPtr, FlowSshC_SftpErr const* error);

	// members
	mutable CriticalSection m_cs;
	bool m_bSuccess;
	SftpErr m_error;

	// friends
	friend class ClientSftpChannel;
};



// ListHandler

class ListHandler
	: public HandlerBase<ListHandler, true>
{
protected:
	// This object must be created on the heap.
	virtual ~ListHandler() {}

public:
	ListHandler();

	bool Success() const;
	// Don't use GetFileInfos before OnDone is called
	std::vector<FileInfo> const& GetFileInfos() const;
	ListErr GetError() const;

protected:
	virtual void OnStart();

	// Callback event handlers
	// Note: handlers are usually called from a worker thread (created in FlowSshC.dll)
	virtual void OnDone() {}

	// Default implementation stores all file listings to m_fileInfos. 
	// You can retrieve the list with GetFileInfos() once OnDone() is called.
	// If you don't need this functionality override OnList with your own implementation.
	//
	// Returning "false" aborts the listing.
	virtual bool OnList(std::vector<FileInfo> const& fileInfos, bool endOfList);
	virtual void OnError(ListErr const& /*error*/) {}

private:
	static bool __cdecl FlowSshC(void* thisPtr, FlowSshC_FileInfo const* fileInfos, unsigned int nrFileInfos, bool endOfList, FlowSshC_ListErr const* error);

	// members
	mutable CriticalSection m_cs;
	bool m_bSuccess;
	ListErr m_error;
	std::vector<FileInfo> m_fileInfos;
	static std::vector<FileInfo> m_emptyFileInfoVector;

	// friends
	friend class ClientSftpChannel;
};



// TransferHandler

class TransferHandler
	: public HandlerBase<TransferHandler, false>
{
protected:
	// This object must be created on the heap.
	virtual ~TransferHandler() {}

public:
	TransferHandler();

	bool Success() const;
	FlowSshC_TransferStat GetTransferStat() const;
	TransferErr GetError() const;

protected:
	virtual void OnStart();

	// Callback event handlers
	// Note: handlers are usually called from a worker thread (created in FlowSshC.dll)
	virtual void OnDone() {}
	
	// Returning "false" aborts the transfer.
	virtual bool OnTransfer(bool /*done*/, FlowSshC_TransferStat const& /*transferStat*/)	{ return true; }
	virtual void OnError(TransferErr const& /*error*/) {}

private:
	static bool __cdecl FlowSshC(void* thisPtr, bool done, FlowSshC_TransferStat const* transferStat, FlowSshC_TransferErr const* error);

	// members
	mutable CriticalSection m_cs;
	bool m_bSuccess;
	TransferErr m_error;
	FlowSshC_TransferStat m_transferStat;

	// friends
	friend class ClientSftpChannel;	
};



// ClientSftpChannel

class ClientSftpChannel : public MainBase
{
protected:
	// This object must be created on the heap.
	virtual ~ClientSftpChannel() {}
	virtual void OnDestroy() const;

public:
	ClientSftpChannel(RefPtrConst<Client> const& client);
	RefPtr<Client> GetClient() { return CastAsNonConst(m_client); }

	void Open(RefPtr<ProgressHandler> const& progress);
	void Open(wchar_t const* subsystem, RefPtr<ProgressHandler> const& progress);
	void RealPath(wchar_t const* queryPath, RefPtr<RealPathHandler> const& realPath);
	void Stat(wchar_t const* path, unsigned int desiredAttrFlags, RefPtr<StatHandler> const& stat);
	void SetStat(wchar_t const* path, FileAttrs const& fileAttrs, RefPtr<SftpHandler> const& response);
	void MkDir(wchar_t const* path, FileAttrs const& fileAttrs, RefPtr<SftpHandler> const& response);
	void RmDir(wchar_t const* path, RefPtr<SftpHandler> const& response);
	void Remove(wchar_t const* path, RefPtr<SftpHandler> const& response);
	void Rename(wchar_t const* oldPath, wchar_t const* newPath, RefPtr<SftpHandler> const& response);
	void List(wchar_t const* path, RefPtr<ListHandler> const& list);
	void Upload(wchar_t const* localPath, wchar_t const* remotePath, unsigned int transferFlags, RefPtr<TransferHandler> const& transfer);
	void Upload(wchar_t const* localPath, wchar_t const* remotePath, unsigned int transferFlags, unsigned int pipelineSize, RefPtr<TransferHandler> const& transfer);
	void Download(wchar_t const* remotePath, wchar_t const* localPath, unsigned int transferFlags, RefPtr<TransferHandler> const& transfer);
	void Download(wchar_t const* remotePath, wchar_t const* localPath, unsigned int transferFlags, unsigned int pipelineSize, RefPtr<TransferHandler> const& transfer);
	void Close(RefPtr<ProgressHandler> const& progress);

protected:
	// Callback event handlers
	// Note: handlers are usually called from a worker thread (created in FlowSshC.dll)
	virtual void OnSftpVersion(unsigned int /*version*/) {}
	virtual void OnChannelClose() {}

private:
	static void __cdecl OnSftpVersion(void* thisPtr, unsigned int version);
	static void __cdecl OnChannelClose(void* thisPtr);	

	// members
	mutable FlowSshC_ClientSftpChannel* m_flowSshC;
	RefPtrConst<Client> m_client; // Channel keeps the associated client alive.
};



// DoneEvent's typedefs

typedef DoneEvent<ProgressHandler>			ProgressEvent;
typedef DoneEvent<ForwardingHandler>		ForwardingEvent;
typedef DoneEvent<ReceiveHandler>			ReceiveEvent;
typedef DoneEvent<RealPathHandler>			RealPathEvent;
typedef DoneEvent<StatHandler>				StatEvent;
typedef DoneEvent<SftpHandler>				SftpEvent;
typedef DoneEvent<ListHandler>				ListEvent;
typedef DoneEvent<TransferHandler>			TransferEvent;



// DoneMsg's typedefs

typedef DoneMsg<ProgressHandler>		ProgressMsg;
typedef DoneMsg<ForwardingHandler>		ForwardingMsg;
typedef DoneMsg<ReceiveHandler>			ReceiveMsg;
typedef DoneMsg<RealPathHandler>		RealPathMsg;
typedef DoneMsg<StatHandler>			StatMsg;
typedef DoneMsg<SftpHandler>			SftpMsg;
typedef DoneMsg<ListHandler>			ListMsg;
typedef DoneMsg<TransferHandler>		TransferMsg;



} // namespace FlowSshCpp
