#pragma once

#include "FlowSshNetCpp.h"


using namespace System;
using namespace System::Collections::Generic;
using namespace System::Threading;
using namespace System::Runtime::InteropServices;



namespace Bitvise {
namespace FlowSshNet {


// Exception - every FlowSshNet call can throw a Exception.

public ref class Exception : public System::Exception 
{
public:
	Exception(String^ msg) : System::Exception(msg) {}
};


// Version

public ref struct Version sealed
{
	unsigned short Major;
	unsigned short Minor;

internal:
	Version(FlowSshC_Version const& x);
};


// Delegates
//
// Parameters: 
//  sender: will be null for fatal errors
//
//	fatal == false:
//		An uncaught exception occurred in your code (for example in Client.OnMyHostKey)
//	fatal == true:
//		Internal error. Terminate the application in response to fatal errors as FlowSshNet 
//		is left in an unusable state. In fact for some errors the core library (FlowSshC) 
//		terminates the process automatically after this event.

public delegate void ExceptionInEventEventHandler(Object^ sender, bool fatal, System::Exception^ e);


// SshNet

public ref class SshNet abstract sealed
{
public:
	static Version^ GetVersion();
	static void SetActCode(String^ actCodeHex);
	static void Shutdown();
	static event ExceptionInEventEventHandler^ OnExceptionInEvent;

internal:
	static void Raise_OnExceptionInEvent(Object^ sender, bool fatal, System::Exception^ e);
};


// MainBase

public ref class MainBase abstract
{};


// WaitImpl

public ref class HandlerBusyException : public FlowSshNet::Exception
{
internal:
	HandlerBusyException(String^ objName);
};


public ref class WaitImpl abstract
{
public:
	property bool IsDisposed { virtual bool get() abstract; }
	property bool IsDone { bool get(); }

	void WaitDone();
	bool WaitDone(int millisecondsTimeout);
	ManualResetEvent^ GetDoneEvent();

internal:
	WaitImpl();
	void Done();	// doneEvent.Set
	void Start();	// doneEvent.Reset

private:
	ManualResetEvent^ m_doneEvent;
};


// Delegates

public delegate void StartEventHandler(Object^ sender);
public delegate void DoneEventHandler(Object^ sender);


// HandlerBase

public ref class ArgumentNullException  : public FlowSshNet::Exception
{
internal:
	ArgumentNullException () : FlowSshNet::Exception("You cannot use a null reference argument here.") {}
};


#pragma warning (push)
#pragma warning (disable: 4692)  // L1: 'function': signature of non-private member contains assembly private native type 'native_type'

template <class T, class C, bool bThrowOnNull>
public ref class HandlerBase abstract : public WaitImpl
{
public:
	event StartEventHandler^ OnStart;
	event DoneEventHandler^  OnDone;

internal:
	// Helpers (static)

	static void S_TryLock(T^ obj)
	{
		if (obj != nullptr) obj->WaitImpl::Start();
		else if (bThrowOnNull) throw gcnew ArgumentNullException();
	}

	static void S_OnStart(T^ obj, MainBase^ const mainObj) { if (obj != nullptr) obj->Base_Raise_OnStart(mainObj); }
	static void S_OnStartFailed(T^ obj) { if (obj != nullptr) obj->Base_OnStartFailed(S_GetFlowSshCpp(obj)); }

	static FlowSshCpp::RefPtr<C> S_GetFlowSshCpp(T^ obj)
	{
		if (obj != nullptr)	return obj->GetFlowSshCpp();
		else return FlowSshCpp::RefPtr<C>(nullptr);
	}

	// Helpers

	void Base_Raise_OnStart(MainBase^ const mainObj)
	{
		assert(mainObj != nullptr);
		m_mainObj = mainObj; // released in Base_Raise_OnDone after OnDone
		try { OnStart(this); }
		catch (System::Exception^ e) { SshNet::Raise_OnExceptionInEvent(this, false, e); }
	}

	void Base_OnStartFailed(FlowSshCpp::RefPtr<C> ptr)
	{
		try { if (ptr.Get() != nullptr) ptr->OnStartFailed(); }
		finally	{ m_mainObj = nullptr; WaitImpl::Done(); }
	}
	
	void Base_Raise_OnDone()
	{
		MainBase^ mainObj = m_mainObj; m_mainObj = nullptr;
		WaitImpl::Done();
		try { OnDone(this); }
		catch (System::Exception^ e) { SshNet::Raise_OnExceptionInEvent(this, false, e); }
		GC::KeepAlive(mainObj);
	}

private:
	MainBase^ m_mainObj;
};

#pragma warning (pop)


// TaskState

public enum class TaskState : unsigned int
{
	InProgress	= 100,
	Completed	= 200,
	Failed		= 300
};


// Delegates

public delegate void ProgressEventHandler(Object^ sender, unsigned int taskSpecificStep);
public delegate void SuccessEventHandler(Object^ sender);
public delegate void ProgressErrorEventHandler(Object^ sender, unsigned int taskSpecificStep, String^ auxInfo);


// ProgressHandler

public ref class ProgressHandler 
	: public HandlerBase<ProgressHandler, ProgressHandlerCpp, false>
{
public:
	ProgressHandler();
	~ProgressHandler();
	!ProgressHandler();

	property bool IsDisposed { virtual bool get() override sealed; }
	property bool Success { bool get(); }

	TaskState	  GetTaskState();
	unsigned int  GetTaskSpecificStep();
	String^		  GetAuxInfo();

	String^ DescribeConnectError();
	String^ DescribeSftpChannelOpenError();

	static String^ DescribeConnectError(unsigned int step, String^ auxInfo);
	static String^ DescribeSftpChannelOpenError(unsigned int step, String^ auxInfo);

	event ProgressEventHandler^      OnProgress;
	event SuccessEventHandler^		 OnSuccess;
	event ProgressErrorEventHandler^ OnError;

internal:
	void Raise_OnProgress(unsigned int taskSpecificStep);
	void Raise_OnSuccess();
	void Raise_OnError(unsigned int taskSpecificStep, String^ auxInfo);
	FlowSshCpp::RefPtr<ProgressHandlerCpp> GetFlowSshCpp();	

private:
	ProgressHandlerCpp* m_flowSshCpp;
};


// DataAllocException

public ref class DataAllocException : public FlowSshNet::Exception
{
internal:
	DataAllocException(String^ msg) : FlowSshNet::Exception(msg) {}
};


// KeyPair

public ref class KeypairLoadException : public FlowSshNet::Exception
{
internal:
	KeypairLoadException(String^ msg) : FlowSshNet::Exception(msg) {}
};

public ref class KeypairExportException : public FlowSshNet::Exception
{
internal:
	KeypairExportException(String^ msg) : FlowSshNet::Exception(msg) {}
};


public ref class Keypair : public MainBase
{
public:
	Keypair(String^ alg, unsigned int bitCount);
	Keypair(array<Byte>^ data, String^ passphrase);
	~Keypair() { this->!Keypair(); }
	!Keypair();
	property bool IsDisposed { bool get(); }

	void SetPassphrase(String^ passphrase);
	array<Byte>^ GetBitviseData();
	array<Byte>^ GetOpenSshData();
	array<Byte>^ GetPuttyData();

	static Keypair^ CreateNew(String^ alg, unsigned int bitCount);
	static Keypair^ CreateFromData(array<Byte>^ data, String^ passphrase);

internal:
	FlowSshCpp::RefPtr<FlowSshCpp::Keypair> GetFlowSshCpp();

private:
	FlowSshCpp::Keypair* m_flowSshCpp;
};


// PublicKey

public ref class PublicKeyLoadException : public Exception
{
internal:
	PublicKeyLoadException(String^ msg) : FlowSshNet::Exception(msg) {}
};


public ref class PublicKey : public MainBase
{
public:
	PublicKey(Keypair^ keypair);
	PublicKey(array<Byte>^ data);
	~PublicKey() { this->!PublicKey(); }
	!PublicKey();
	property bool IsDisposed { bool get(); }

	unsigned int GetBitCount();
	unsigned int GetEffectiveSecurity();
	bool         IsEqual(PublicKey^ other);
	String^		 GetAlg();
	String^		 GetMd5();
	String^		 GetSha256();
	String^		 GetBubbleBabble();
	array<Byte>^ GetSsh2Data();
	array<Byte>^ GetOpenSshData();

	static PublicKey^ CreateFromKeypair(Keypair^ keypair);
	static PublicKey^ CreateFromData(array<Byte>^ data);

internal:
	PublicKey(FlowSshCpp::RefPtr<FlowSshCpp::PublicKey> publicKey);
	FlowSshCpp::RefPtr<FlowSshCpp::PublicKey> GetFlowSshCpp();

private:
	FlowSshCpp::PublicKey* m_flowSshCpp;
};


// FurtherAuth
//
// FurtherAuth can only be used during calls to OnFurtherAuth event handlers.
// It throws ObjectDisposedException if used outside of this context.

public ref class FurtherAuth sealed
{
public:
	property bool IsDisposed { bool get(); }

	void SetUserName(String^ userName);
	void SetPassword(String^ password);
	void SetKeypair(Keypair^ keypair);

	bool HavePartialSuccess();
	bool IsPasswordRemaining();
	bool IsPublicKeyRemaining();

internal:
	FurtherAuth(FlowSshCpp::Client::FurtherAuth* flowCpp);
	void ResetFlowSshCpp();

private:
	FlowSshCpp::Client::FurtherAuth* volatile m_flowSshCpp;
};


// PasswordChange
//
// PasswordChange can only be used during calls to OnPasswordChange event handlers.
// It throws ObjectDisposedException if used outside of this context.

public ref class PasswordChange sealed
{
public:
	property bool IsDisposed { bool get(); }

	String^ GetPrompt();
	void	SetNewPassword(String^ password);

internal:
	PasswordChange(FlowSshCpp::Client::PasswordChange* flowCpp);
	void ResetFlowSshCpp();

private:
	FlowSshCpp::Client::PasswordChange* volatile m_flowSshCpp;
};


// ConnectStep

public enum class ConnectStep : unsigned int
{
	ConnectToProxy		= 100,	// Skipped when connecting directly.
	ConnectToSshServer	= 200,
	SshVersionString	= 300,
	SshKeyExchange		= 400,
	SshUserAuth			= 500
};


// DisconnectReason

public enum class DisconnectReason : unsigned int
{
	Exception			= 0,
	FlowError			= 1,
	ConnectionError		= 2,
	ConnectionLost		= 3,
	ByServer			= 4,
	ByClient			= 5
};


// ProxyType

public enum class ProxyType : unsigned int
{
	None				= 0,
	Socks4				= 1,
	Socks5				= 2,
	HttpConnect			= 3
};


// Algorithms

public ref struct KeyExchangeAlgs sealed
{
	Byte   MlKem1024nistp384;	// mlkem1024nistp384-sha384
	Byte   MlKem768x25519;		// mlkem768x25519-sha256

	Byte   Curve25519;			// curve25519-sha256, curve25519-sha256@libssh.org
	Byte   EcdhSecp256k1;		// ecdh-sha2-1.3.132.0.10
	Byte   EcdhNistp512;		// ecdh-sha2-nistp521
	Byte   EcdhNistp384;		// ecdh-sha2-nistp384
	Byte   EcdhNistp256;		// ecdh-sha2-nistp256

	UInt32 GexBitsMin;
	UInt32 GexBitsOpt;
	UInt32 GexBitsMax;

	Byte   DhG16Sha512;			// diffie-hellman-group16-sha512
	Byte   DhG15Sha512;			// diffie-hellman-group15-sha512
	Byte   DhG14Sha256;			// diffie-hellman-group14-sha256
	Byte   DhG14Sha1;			// diffie-hellman-group14-sha1
	Byte   DhG1Sha1;			// diffie-hellman-group1-sha1
	Byte   DhGexSha256;			// diffie-hellman-group-exchange-sha256
	Byte   DhGexSha1;			// diffie-hellman-group-exchange-sha1

	KeyExchangeAlgs();

internal:
	FlowSshCpp::KeyExchangeAlgs const ToFlowCpp();
};

public ref struct HostKeyAlgs sealed
{
	Byte RsaSha2_512;		// rsa-sha2-512
	Byte RsaSha2_256;		// rsa-sha2-256
	Byte Ed25519;			// ssh-ed25519
	Byte EcdsaSecp256k1;	// ecdsa-sha2-1.3.132.0.10
	Byte EcdsaNistp521;		// ecdsa-sha2-nistp521
	Byte EcdsaNistp384;		// ecdsa-sha2-nistp384
	Byte EcdsaNistp256;		// ecdsa-sha2-nistp256
	Byte SshRsa;			// ssh-rsa
	Byte SshDss;			// ssh-dsa
	HostKeyAlgs();

internal:
	FlowSshCpp::HostKeyAlgs const ToFlowCpp();
};

public ref struct EncryptionAlgs sealed
{
	Byte ChaCha20Poly1305;	// chacha20-poly1305@openssh.com
	Byte Aes256Gcm;			// aes256-gcm@openssh.com
	Byte Aes128Gcm;			// aes128-gcm@openssh.com
	Byte Aes256Ctr;			// aes256-ctr
	Byte Aes192Ctr;			// aes192-ctr
	Byte Aes128Ctr;			// aes128-ctr
	Byte TripleDesCtr;		// 3des-ctr
	Byte Aes256Cbc;			// aes256-cbc
	Byte Aes192Cbc;			// aes192-cbc
	Byte Aes128Cbc;			// aes128-cbc
	Byte TripleDesCbc;		// 3des-cbc
	EncryptionAlgs();

internal:
	FlowSshCpp::EncryptionAlgs const ToFlowCpp();
};

public ref struct MacAlgs sealed
{
	Byte HmacSha2_256_Etm;	// hmac-sha2-256-etm@openssh.com
	Byte HmacSha2_512_Etm;	// hmac-sha2-512-etm@openssh.com
	Byte HmacSha1_Etm;		// hmac-sha1-etm@openssh.com
	Byte HmacSha2_256;		// hmac-sha2-256
	Byte HmacSha2_512;		// hmac-sha2-512
	Byte HmacSha1;			// hmac-sha1	
	MacAlgs();

internal:
	FlowSshCpp::MacAlgs const ToFlowCpp();
};

public ref struct CompressionAlgs sealed
{
	Byte Zlib;				// zlib
	Byte None;				// none
	CompressionAlgs();

internal:
	FlowSshCpp::CompressionAlgs const ToFlowCpp();
};

public enum class Elevation : unsigned int
{
	Default		= 0,
	Yes			= 1,
	No			= 2,
};

public enum class SendExtInfo : unsigned int
{
	Default		= 0,
	Yes			= 1,
	No			= 2,
};

public enum class NoFlowControl : unsigned int
{
	Unsupported	= 0,
	Supported	= 1,
	Preferred	= 2,
};

public enum class GlobalRequests : unsigned int
{
	AutoDetect	= 0,
	Supported	= 1,
	Unsupported	= 2,
};

public ref struct Options sealed
{
	bool StartKeyReExchange;
	int SendBasedKeepAliveTimeoutMs;
	int RecvBasedKeepAliveTimeoutMs;
	int RecvBasedKeepAliveRelaxedTimeoutMs;
	int RecvBasedUnresponsivenessTimeoutMs;
	Elevation Elevation;
	SendExtInfo SendExtInfo;
	NoFlowControl NoFlowControl;
	GlobalRequests GlobalRequests;
	bool RequireStrictKex;
	Options();

internal:
	FlowSshCpp::Options const ToFlowCpp();
};

public enum class SessionIdReplyCode : unsigned int
{
	Undisclosed				= 0,
	SessionUnique			= 1,
	ConnectionsTerminated	= 2,
	TimeoutExpired			= 3,
};


// ForwardingErrCode

public enum class ForwardingErrCode : unsigned int
{
	GeneralError		= 2,
	ClientNotConnected	= 3,
	AddressInUse		= 4,
	RejectedByServer	= 5,
	RuleNotFound		= 6,
	AlreadyInvited		= 7
};


// ForwardingRule

public ref struct ForwardingRuleRef
{
	// S2C: When forwarding from a Unix socket, ListInterface must start with '/'
	// and ListPort must be set to ForwardingRuleRef::Port_UnixSocket.

	bool ClientToServer;
	String^ ListInterface;
	unsigned int ListPort;

	literal unsigned int Port_UnixSocket = ((unsigned int)(-1));

internal:
	FlowSshCpp::ForwardingRuleRef const ToFlowCpp();
};

public ref struct ForwardingRule : public ForwardingRuleRef
{
	// C2S: When forwarding to a Unix socket, DestHost must start with '/'
	// and DestPort must be set to ForwardingRuleRef::Port_UnixSocket.

	String^ DestHost;
	unsigned int DestPort;

internal:
	FlowSshCpp::ForwardingRule const ToFlowCpp() new;
};

public ref struct ForwardingRuleExt sealed : public ForwardingRule
{	
	String^ ConnectInterface;
	
internal:
	FlowSshCpp::ForwardingRuleExt const ToFlowCpp() new;
};


// ProxyForwarding

public ref struct ProxyForwarding
{
	String^ ListInterface;
	unsigned int ListPort;
	String^ BindPublicAddressIP4;
	String^ BindPublicAddressIP6;
	String^ BindListInterfaceIP4;
	String^ BindListInterfaceIP6;

internal:
	FlowSshCpp::ProxyForwarding const ToFlowCpp();
};


// ForwardingErr

public ref struct ForwardingErr sealed
{
	ForwardingErrCode ErrCode;
	String^ AuxInfo;
	String^ Desc;

internal:
	ForwardingErr(FlowSshCpp::ForwardingErr const& x);
};


// Delegates

public delegate void ForwardingSuccessEventHandler(Object^ sender, unsigned int listPort);
public delegate void ForwardingErrorEventHandler(Object^ sender, ForwardingErr^ error);


// ForwardingHandler

public ref class ForwardingHandler
	: public HandlerBase<ForwardingHandler, ForwardingHandlerCpp, false>
{
public:
	ForwardingHandler();
	~ForwardingHandler();
	!ForwardingHandler();

	property bool  IsDisposed { virtual bool get() override sealed; }
	property bool  Success { bool get(); }

	unsigned int   GetListPort();
	ForwardingErr^ GetError();

	event ForwardingSuccessEventHandler^ OnSuccess;
	event ForwardingErrorEventHandler^   OnError;

internal:
	void Raise_OnSuccess(unsigned int listPort);
	void Raise_OnError(ForwardingErr^ error);
	FlowSshCpp::RefPtr<ForwardingHandlerCpp> GetFlowSshCpp();

private:
	ForwardingHandlerCpp* m_flowSshCpp;
};


// ForwardingLog

public ref struct ForwardingLog
{
	enum class Event : unsigned int
	{
		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 Event;
	String^ Desc;

internal:
	static ForwardingLog^ Create(FlowSshCpp::RefPtrConst<FlowSshCpp::ForwardingLog> const& x);
	ForwardingLog(FlowSshCpp::ForwardingLog const& x);
};

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

	unsigned int Type;
	String^ SrvSideRuleDesc;
	String^ ListInterface;
	unsigned int ListPort;
	String^ OrigAddress;
	unsigned int OrigPort;
	String^ DestAddress;
	unsigned int DestPort;

internal:
	ForwardingLog_X2Y(FlowSshCpp::ForwardingLog_X2Y const& x);
};

public ref struct ForwardingLog_X2YOpenFailed : public ForwardingLog_X2Y
{
	String^ AuxInfo;

internal:
	ForwardingLog_X2YOpenFailed(FlowSshCpp::ForwardingLog_X2YOpenFailed const& x);
};

public ref struct ForwardingLog_X2YClosed : public  ForwardingLog_X2Y
{
	unsigned __int64 BytesSent;
	unsigned __int64 BytesReceived;

internal:
	ForwardingLog_X2YClosed(FlowSshCpp::ForwardingLog_X2YClosed 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;

public ref struct ForwardingLog_ServerSideC2S : public ForwardingLog
{
	String^ ListInterface;
	unsigned int ListPort;
	String^ RuleDesc;

internal:
	ForwardingLog_ServerSideC2S(FlowSshCpp::ForwardingLog_ServerSideC2S const& x);
};

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

	unsigned int ErrCode;
	String^ AuxInfo;

internal:
	ForwardingLog_ServerSideC2SAddFailed(FlowSshCpp::ForwardingLog_ServerSideC2SAddFailed const& x);
};
	
public ref struct ForwardingLog_Proxy : public ForwardingLog
{
	String^ ProxyListInterface;
	unsigned int ProxyListPort;
	String^ ProxyOrigAddress;
	unsigned int ProxyOrigPort;

internal:
	ForwardingLog_Proxy(FlowSshCpp::ForwardingLog_Proxy const& x);
};

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

	unsigned int ErrCode;
	String^ AuxInfo;

internal:
	ForwardingLog_ProxyDecodeFailed(FlowSshCpp::ForwardingLog_ProxyDecodeFailed const& x);
};

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

	unsigned int ProxyType;
	String^ ProxyReqAddress;
	unsigned int ProxyReqPort;

internal:
	ForwardingLog_ProxyStarted(FlowSshCpp::ForwardingLog_ProxyStarted const& x);
};

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

	unsigned int ErrCode;
	String^ AuxInfo;

internal:
	ForwardingLog_ProxyBindFailed(FlowSshCpp::ForwardingLog_ProxyBindFailed const& x);
};

public ref struct ForwardingLog_ProxyBindStarted : public ForwardingLog_ProxyStarted
{
	String^ BindPublicAddress;
	unsigned int BindPublicPort;
	String^ BindListInterface;
	unsigned int BindListPort;

internal:
	ForwardingLog_ProxyBindStarted(FlowSshCpp::ForwardingLog_ProxyBindStarted const& x);
};

public ref struct ForwardingLog_ProxyBindAborted : public ForwardingLog_ProxyBindStarted
{
	enum class AbrtCode : unsigned int
	{
		ConnectionEof		= 0,
		ConnectionError		= 1,
		AcceptTimeout		= 2
	};
		
	unsigned int AbrtCode;
	String^ AuxInfo;

internal:
	ForwardingLog_ProxyBindAborted(FlowSshCpp::ForwardingLog_ProxyBindAborted const& x);
};


// Delegates

public delegate void SshVersionEventHandler(Object^ sender, String^ version);
public delegate bool HostKeyEventHandler(Object^ sender, PublicKey^ publicKey);
public delegate void KexDoneEventHandler(Object^ sender, unsigned __int64 kexNr, bool incoming, String^ kexAlg, String^ encAlg, String^ macAlg, String^ cmprAlg);
public delegate bool FurtherAuthEventHandler(Object^ sender, FurtherAuth^ furtherAuth);
public delegate bool PasswordChangeEventHandler(Object^ sender, PasswordChange^ passwordChange);
public delegate void BannerEventHandler(Object^ sender, String^ banner);
public delegate void SessionIdReplyEventHandler(Object^ sender, unsigned int code);
public delegate void ServerNoticeEventHandler(Object^ sender, unsigned int severity, String^ facility, String^ status, String^ description);
public delegate bool HostKeySyncEventHandler(Object^ sender, PublicKey^ publicKey, bool keyVerified);
public delegate void ForwardingLogHandler(Object^ sender, ForwardingLog^ log);
public delegate void DisconnectEventHandler(Object^ sender, DisconnectReason reason, String^ desc);


// ClientBase

public ref class ClientBase : public MainBase
{
public:
	ClientBase();
	~ClientBase();
	!ClientBase();
	property bool IsDisposed { bool get(); }

	void SetAppName(String^ appNameAndVersion);
	void SetDebugFile(String^ debugFile, unsigned int debugEventMask);

	void SetProxyType(ProxyType type);
	void SetProxyHost(String^ host);
	void SetProxyPort(unsigned int port);
	void SetProxyUserName(String^ userName);
	void SetProxyPassword(String^ password);
	void SetProxyOptions(bool resolveLocally);

	void SetHost(String^ host);
	void SetPort(unsigned int port);
	void SetObfuscation(bool enable, String^ keyword);
	void SetUserName(String^ userName);
	void SetPassword(String^ password);
	void SetKeypair(Keypair^ keypair);
	void SetSessionId(String^ clientSessionId, unsigned int timeoutMs);

	void SetKeyExchangeAlgs(KeyExchangeAlgs^ algs);
	void SetHostKeyAlgs(HostKeyAlgs^ algs);
	void SetEncryptionAlgs(EncryptionAlgs^ algs);
	void SetMacAlgs(MacAlgs^ algs);
	void SetCompressionAlgs(CompressionAlgs^ algs);
	void SetOptions(Options^ opts);

	void SetSocketProvider(unsigned int socketProvider);
	void SetPreferredIPVersion(unsigned int version);
	void SetConnectInterface(String^ interfaceList);

	void Connect(ProgressHandler^ progress);
	void Disconnect(ProgressHandler^ progress);

	void AddForwarding(ForwardingRule^ rule, ForwardingHandler^ response);
	void AddForwarding(ForwardingRuleExt^ rule, ForwardingHandler^ response);
	void CancelForwarding(ForwardingRuleRef^ ruleRef, ForwardingHandler^ response);
	void InviteForwardings(bool clientToServer, ForwardingHandler^ response);
	void EnableProxyForwarding(ProxyForwarding^ settings, ForwardingHandler^ response);
	void DisableProxyForwarding(ForwardingHandler^ response);

	event SshVersionEventHandler^	  OnSshVersion;
	event HostKeyEventHandler^		  OnHostKey;
	event KexDoneEventHandler^        OnKexDone;
	event FurtherAuthEventHandler^	  OnFurtherAuth;
	event PasswordChangeEventHandler^ OnPasswordChange;
	event BannerEventHandler^		  OnBanner;
	event SessionIdReplyEventHandler^ OnSessionIdReply;
	event ServerNoticeEventHandler^   OnServerNotice;
	event HostKeySyncEventHandler^	  OnHostKeySync;
	event ForwardingLogHandler^		  OnForwardingLog;
	event DisconnectEventHandler^	  OnDisconnect;

internal:
	void Raise_OnSshVersion(String^ version);
	bool Raise_OnHostKey(PublicKey^ publicKey);
	void Raise_OnKexDone(unsigned __int64 kexNr, bool incoming, String^ kexAlg, String^ encAlg, String^ macAlg, String^ cmprAlg);
	bool Raise_OnFurtherAuth(FurtherAuth^ furtherAuth);
	bool Raise_OnPasswordChange(PasswordChange^ passwordChange);
	void Raise_OnBanner(String^ banner);
	void Raise_OnSessionIdReply(unsigned int code);
	void Raise_OnServerNotice(unsigned int severity, String^ facility, String^ status, String^ description);
	bool Raise_OnHostKeySync(PublicKey^ publicKey, bool keyVerified);
	void Raise_OnForwardingLog(ForwardingLog^ log);
	void Raise_OnDisconnect(DisconnectReason reason, String^ desc);
	FlowSshCpp::RefPtr<ClientCpp> GetFlowSshCpp();

private:
	ClientCpp* m_flowSshCpp;
};


// Client

public ref class Client : public ClientBase
{
public:
	Client();

	void ClearHostKeyState();	
	void AcceptHostKey(PublicKey^ pk);
	void AcceptHostKeyBB(String^ fp);
	void AcceptHostKeyMd5(String^ fp);
	void AcceptHostKeySha256(String^ fp);
	PublicKey^ GetServerHostKey();

private:
	void BuiltIn_OnSshVersion(Object^ sender, String^ version);
	bool BuiltIn_OnHostKey(Object^ sender, PublicKey^ publicKey);
	void BuiltIn_OnKexDone(Object^ sender, unsigned __int64 kexNr, bool incoming, String^ kexAlg, String^ encAlg, String^ macAlg, String^ cmprAlg);
	bool BuiltIn_OnFurtherAuth(Object^ sender, FurtherAuth^ furtherAuth);
	bool BuiltIn_OnPasswordChange(Object^ sender, PasswordChange^ passwordChange);
	void BuiltIn_OnBanner(Object^ sender, String^ banner);
	void BuiltIn_OnSessionIdReply(Object^ sender, unsigned int code);
	void BuiltIn_OnServerNotice(Object^ sender, unsigned int severity, String^ facility, String^ status, String^ description);
	bool BuiltIn_OnHostKeySync(Object^ sender, PublicKey^ publicKey, bool keyVerified);
	void BuiltIn_OnForwardingLog(Object^ sender, ForwardingLog^ log);
	void BuiltIn_OnDisconnect(Object^ sender, DisconnectReason reason, String^ desc);

private:
	List<PublicKey^>^ m_acceptHostKeys_keys;
	List<String^>^ m_acceptHostKeys_bb;
	List<String^>^ m_acceptHostKeys_md5;
	List<String^>^ m_acceptHostKeys_sha256;
	PublicKey^ m_serverHostKey;
};


// Delegates

public delegate void ReceiveEventHandler(Object^ sender, bool stdErr, array<Byte>^ data, bool eof);
public delegate void ReceiveErrorEventHandler(Object^ sender);


// ReceiveHandler

public ref class ReceiveHandler
	: public HandlerBase<ReceiveHandler, ReceiveHandlerCpp, false>
{
public:
	ReceiveHandler();
	~ReceiveHandler();
	!ReceiveHandler();

	property bool IsDisposed { virtual bool get() override sealed; }
	property bool Success { bool get(); }

	bool		  StdErr();
	bool		  Eof();
	array<Byte>^  GetData();

	event ReceiveEventHandler^		OnReceive;
	event ReceiveErrorEventHandler^ OnError;

internal:
	void Raise_OnReceive(bool stdErr, array<Byte>^ data, bool eof);
	void Raise_OnError();
	FlowSshCpp::RefPtr<ReceiveHandlerCpp> GetFlowSshCpp();

private:
	ReceiveHandlerCpp* m_flowSshCpp;
};


// ExitStatus

public ref struct ExitStatus sealed
{
	unsigned int Code;

internal:
	ExitStatus(FlowSshC_ExitStatus const& x);
};


// ExitSignal

public ref struct ExitSignal sealed
{	
	String^ Name; 
	bool	CoreDumped;
	String^ ErrMsg;

internal:
	ExitSignal(FlowSshCpp::ClientSessionChannel::ExitSignal const& x);
};


// Delegates

public delegate void ExitStatusEventHandler(Object^ sender, ExitStatus^ status);
public delegate void ExitSignalEventHandler(Object^ sender, ExitSignal^ signal);
public delegate void ChannelCloseEventHandler(Object^ sender);


// ClientSessionChannel

public ref class ClientSessionChannel : public MainBase
{
public:
	ClientSessionChannel(ClientBase^ client);
	~ClientSessionChannel();
	!ClientSessionChannel();
	property bool IsDisposed { bool get(); }
	ClientBase^ GetClient();

	void OpenRequest(ProgressHandler^ progress);
	void PtyRequest(String^ term, unsigned int widthCols, unsigned int heightRows, ProgressHandler^ progress);
	void ExecRequest(String^ command, ProgressHandler^ progress);
	void ShellRequest(ProgressHandler^ progress);
	void Receive(ReceiveHandler^ receive);
	void Receive(unsigned int maxBytes, ReceiveHandler^ receive);
	void Send(array<Byte>^ data, bool eof, ProgressHandler^ progress);
	void Send(array<Byte>^ data, int offset, int size, bool eof, ProgressHandler^ progress);
	void Signal(String^ signalName, ProgressHandler^ progress);
	void Close(ProgressHandler^ progress);

	event ExitStatusEventHandler^	OnExitStatus;
	event ExitSignalEventHandler^   OnExitSignal;
	event ChannelCloseEventHandler^ OnChannelClose;

internal:
	void Raise_OnExitStatus(ExitStatus^ status);
	void Raise_OnExitSignal(ExitSignal^ signal);
	void Raise_OnChannelClose();
	FlowSshCpp::RefPtr<ClientSessionChannelCpp> GetFlowSshCpp();

private:
	ClientSessionChannelCpp* m_flowSshCpp;
	ClientBase^ m_client; // Channel keeps the associated client alive.
};


// FileType

public enum class FileType : unsigned int
{
	Regular		= 1,
	Directory	= 2,
	Symlink		= 3,
	Special		= 4,
	Unknown		= 5,
	// SFTPv6+
	Socket		= 6,
	CharDevice	= 7,
	BlockDevice = 8,
	Fifo		= 9
};


// AttrFlags

[Flags] public enum class AttrFlags : unsigned int
{
	Size		= 0x00000001,
	UidGid		= 0x00000002, // SFTPv3-
	Permissions	= 0x00000004,
	// SFTPv4+
	AccessTime	= 0x00000008, // Available in SFTPv3- together with ModifyTime
	CreateTime	= 0x00000010, 
	ModifyTime	= 0x00000020, // Available in SFTPv3- together with AccessTime
	OwnerGroup	= 0x00000080,	
	Subseconds	= 0x00000100,
	// SFTPv6+
	AllocSize	= 0x00000400, 
	TextHint	= 0x00000800
};


// TextHint

public enum class TextHint : unsigned int
{
	KnownText		= 0,
	GuessedText		= 1,
	KnownBinary		= 2,
	GuessedBinary	= 3
};


// FileAttrs

public ref struct FileAttrs sealed
{
	AttrFlags ValidAttrFlags;				// AttrFlags mask of file attributes available
	FileType Type;							// Always present
	unsigned __int64 Size;					// AttrFlags::Size
	unsigned int Uid; unsigned int Gid;		// AttrFlags::UidGid
	unsigned int Permissions;				// AttrFlags::Permissions
	DateTime AccessTime;					// AttrFlags::AccessTime (&& FlowSshC_AttrFlags::Subseconds for milliseconds)
	DateTime CreateTime;					// AttrFlags::CreateTime (&& FlowSshC_AttrFlags::Subseconds for milliseconds)
	DateTime ModifyTime;					// AttrFlags::ModifyTime (&& FlowSshC_AttrFlags::Subseconds for milliseconds)
	String^ Owner; String^ Group;			// AttrFlags::OwnerGroup
	unsigned __int64 AllocSize;				// AttrFlags::AllocSize
	TextHint TextHint;						// AttrFlags::TextHint
	FileAttrs() {}

internal:
	FileAttrs(FlowSshCpp::FileAttrs const& x);
	FlowSshCpp::FileAttrs const ToFlowCpp();
};


// FileInfo

public ref struct FileInfo sealed
{
	String^	   Name;
	FileAttrs^ Attrs;

internal:
	FileInfo(FlowSshCpp::FileInfo const& x);
};


// SftpErrCode

public enum class SftpErrCode : unsigned int
{
	Eof						= 1,
	NoSuchFile				= 2,
	PermissionDenied		= 3,
	Failure					= 4,
	BadMessage				= 5,
	NoConnection			= 6,
	ConnectionLost			= 7,
	OpUnsupported			= 8,
	// SFTP4+
	InvalidHandle			= 9,
	NoSuchPath				= 10,
	FileAlreadyExists		= 11,
	WriteProtect			= 12,
	NoMedia					= 13,
	// SFTP5+
	NoSpaceOnFilesystem		= 14,
	QuotaExceeded			= 15,
	UnknownPrincipal		= 16,
	LockConflict			= 17,
	// SFTP6+
	DirNotEmpty				= 18,
	NotADirectory			= 19,
	InvalidFilename			= 20,
	LinkLoop				= 21,
	CannotDelete			= 22,
	InvalidParameter		= 23,
	FileIsADirectory		= 24,
	ByteRangeLockConflict	= 25,
	ByteRangeLockRefused	= 26,
	DeletePending			= 27,
	FileCorrupt				= 28,
	OwnerInvalid			= 29,
	GroupInvalid			= 30,
	NoMatchingByteRangeLock	= 31
};


// SftpErr

public ref struct SftpErr sealed
{
	SftpErrCode ErrCode;
	String^		ErrMsg;

	String^ Describe();

internal:
	SftpErr(FlowSshCpp::SftpErr const& x);
};


// ListOp

public enum class ListOp : unsigned int
{
	// Codeless errors
	Exception		= 0,
	// SFTP errors - FlowSshC_SftpErrCode
	ListDir			= 100,
	ChannelNotOpen	= 150	// If channel is closed, closing, or opening.
}; 


// ListErr

public ref struct ListErr sealed
{
	ListOp		 ListOp;
	unsigned int ErrCode; 
	String^		 ErrMsg;

	String^ Describe();
	
internal:
	ListErr(FlowSshCpp::ListErr const& x);
};


// TransferOp

public enum class TransferOp : unsigned int
{
	// Codeless erros
	Exception				= 0,
	// SFTP errors - FlowSshC_SftpErrCode
	MakeRemoteDir			= 100,
	OpenRemoteFile			= 101,
	ReadRemoteFile			= 102,
	WriteRemoteFile			= 103,
	ChannelNotOpen			= 150,		// If channel is closed, closing, or opening.
	// WinAPI errors - see MSDN
	MakeLocalDir			= 200,
	OpenLocalFile			= 201,
	ReadLocalFile			= 202,
	WriteLocalFile			= 203,
	// Resume errors - FlowSshC_ResumeErrCode
	ResumeLocalFile			= 300,
	ResumeRemoteFile		= 301
};


// ResumeErrCode

public enum class ResumeErrCode : unsigned int
{
	TextMode	  = 1,
	FileSize	  = 2,
	Conversion	  = 3,
	Content		  = 4,
	FileSizeError = 5
};


// TransferErr

public ref struct TransferErr sealed
{
	TransferOp	 TransferOp;
	unsigned int ErrCode; 
	String^		 ErrMsg;

	String^ Describe();
	
internal:
	TransferErr(FlowSshCpp::TransferErr const& x);
};


// TransferStat

public ref struct TransferStat sealed
{
	unsigned __int64 BytesTransferred;
	unsigned __int64 TimeElapsedMs;
	unsigned __int64 CurrentFileSize;
	unsigned __int64 FinalFileSize;
	bool			 FinalFileSizeValid;

internal:
	TransferStat(FlowSshC_TransferStat const& x);
};


// Delegates

public delegate void RealPathEventHandler(Object^ sender, String^ realPath);
public delegate void SftpErrorEventHandler(Object^ sender, SftpErr^ error);


// RealPathHandler

public ref class RealPathHandler 
	: public HandlerBase<RealPathHandler, RealPathHandlerCpp, true>
{
public:
	RealPathHandler();
	~RealPathHandler();
	!RealPathHandler();

	property bool IsDisposed { virtual bool get() override sealed; }
	property bool Success { bool get(); }

	String^		  GetRealPath();
	SftpErr^	  GetError();	

	event RealPathEventHandler^	 OnRealPath;
	event SftpErrorEventHandler^ OnError;

internal:
	void Raise_OnRealPath(String^ realPath);
	void Raise_OnError(SftpErr^ error);
	FlowSshCpp::RefPtr<RealPathHandlerCpp> GetFlowSshCpp();
	
private:
	RealPathHandlerCpp* m_flowSshCpp;
};


// Delegates

public delegate void StatEventHandler(Object^ sender, FileAttrs^ fileAttrs);


// StatHandler

public ref class StatHandler 
	: public HandlerBase<StatHandler, StatHandlerCpp, true>
{
public:
	StatHandler();
	~StatHandler();
	!StatHandler();

	property bool IsDisposed { virtual bool get() override sealed; }
	property bool Success { bool get(); }

	FileAttrs^	  GetFileAttrs();
	SftpErr^	  GetError();

	event StatEventHandler^		 OnStat;
	event SftpErrorEventHandler^ OnError;

internal:
	void Raise_OnStat(FileAttrs^ fileAttrs);
	void Raise_OnError(SftpErr^ error);
	FlowSshCpp::RefPtr<StatHandlerCpp> GetFlowSshCpp();
	
private:	
	StatHandlerCpp* m_flowSshCpp;	
};


// SftpHandler

public ref class SftpHandler 
	: public HandlerBase<SftpHandler, SftpHandlerCpp, false>
{
public:
	SftpHandler();
	~SftpHandler();
	!SftpHandler();
	
	property bool IsDisposed { virtual bool get() override sealed; }
	property bool Success { bool get(); }
	SftpErr^	  GetError();

	event SuccessEventHandler^   OnSuccess;
	event SftpErrorEventHandler^ OnError;

internal:
	void Raise_OnSuccess();
	void Raise_OnError(SftpErr^ error);
	FlowSshCpp::RefPtr<SftpHandlerCpp> GetFlowSshCpp();
	
private:
	SftpHandlerCpp* m_flowSshCpp;
};


// Delegates

public delegate bool ListEventHandler(Object^ sender, array<FileInfo^>^ fileInfos, bool endOfList);
public delegate void ListErrorEventHandler(Object^ sender, ListErr^ error);


// ListHandler

public ref class ListHandler 
	: public HandlerBase<ListHandler, ListHandlerCpp, true>
{
public:
	// useGetFileInfos: 
	//	 TRUE  > file listings are stored; after OnDone they can be retrieved with GetFileInfos()
	//	 FALSE > file listings not stored
	ListHandler(bool useGetFileInfos);
	~ListHandler();
	!ListHandler();

	property bool IsDisposed { virtual bool get() override sealed; }
	property bool Success { bool get(); }

	array<FileInfo^>^ GetFileInfos(); // returns empty array if called before OnDone
	ListErr^	      GetError();

	event ListEventHandler^		 OnList
	{
		void add(ListEventHandler^ del);
		void remove(ListEventHandler^ del);
	protected:
		bool raise(Object^ sender, array<FileInfo^>^ fileInfos, bool endOfList);
	}
	event ListErrorEventHandler^ OnError;

internal:
	void BuiltIn_OnStart(Object^ sender);
	bool Raise_OnList(array<FileInfo^>^ fileInfos, bool endOfList);
	void Raise_OnError(ListErr^ error);
	FlowSshCpp::RefPtr<ListHandlerCpp> GetFlowSshCpp();

private:
	ListEventHandler^ m_del;
	array<FileInfo^>^ m_fileInfos;
	ListHandlerCpp* m_flowSshCpp;
};


// Delegates

public delegate bool TransferEventHandler(Object^ sender, bool done, TransferStat^ transferStat);
public delegate void TransferErrorEventHandler(Object^ sender, TransferErr^ error);


// TransferHandler

public ref class TransferHandler 
	: public HandlerBase<TransferHandler, TransferHandlerCpp, false>
{
public:
	TransferHandler();
	~TransferHandler();
	!TransferHandler();

	property bool IsDisposed { virtual bool get() override sealed; }
	property bool Success { bool get(); }

	TransferStat^ GetTransferStat();
	TransferErr^  GetError();

	event TransferEventHandler^      OnTransfer
	{
		void add(TransferEventHandler^ del);
		void remove(TransferEventHandler^ del);
	protected:
		bool raise(Object^ sender, bool done, TransferStat^ transferStat);
	}
	event TransferErrorEventHandler^ OnError;

internal:
	bool Raise_OnTransfer(bool done, TransferStat^ transferStat);
	void Raise_OnError(TransferErr^ error);
	FlowSshCpp::RefPtr<TransferHandlerCpp> GetFlowSshCpp();	

private:
	TransferEventHandler^ m_del;
	TransferHandlerCpp* m_flowSshCpp;
};


// TransferFlags

[Flags] public enum class TransferFlags : unsigned int
{
	Binary           = 0x00000000,
	AutoLf           = 0x00000001,
	AutoStd          = 0x00000002, // SFTPv4+
	TextStd          = 0x00000003, // SFTPv4+
	Text             = 0x00000003, // SFTPv4+: alias for TextStd
	TextLf           = 0x00000004,
	Resume           = 0x00000010,
	Overwrite        = 0x00000020,
	Move             = 0x00000040,
	AutoMkDir        = 0x00000080,
	ResumeVerifiable = 0x00000100,
	NoSync           = 0x00000200,
	NoBuf_PreferYes  = 0x00000400,
	NoBuf_PreferNo   = 0x00000800
};


// ClientSftpChannelOpenStep

public enum class ClientSftpChannelOpenStep  : unsigned int
{
	OpenRequest	= 100,
	SftpRequest	= 200,
	InitPacket	= 300
};


// Delegates

public delegate void SftpVersionEventHandler(Object^ sender, unsigned int version);


// ClientSftpChannel

public ref class ClientSftpChannel : public MainBase
{
public:
	ClientSftpChannel(ClientBase^ client);
	~ClientSftpChannel();
	!ClientSftpChannel();
	property bool IsDisposed { bool get(); }
	ClientBase^ GetClient();

	void Open(ProgressHandler^ progress);
	void Open(String^ subsystem, ProgressHandler^ progress);
	void RealPath(String^ queryPath, RealPathHandler^ realPath);
	void Stat(String^ path, AttrFlags desiredAttrFlags, StatHandler^ stat);
	void SetStat(String^ path, FileAttrs^ fileAttrs, SftpHandler^ response);
	void MkDir(String^ path, FileAttrs^ fileAttrs, SftpHandler^ response);
	void RmDir(String^ path, SftpHandler^ response);
	void Remove(String^ path, SftpHandler^ response);
	void Rename(String^ oldPath, String^ newPath, SftpHandler^ response);
	void List(String^ path, ListHandler^ list);
	void Upload(String^ localPath, String^ remotePath, TransferFlags transferFlags, TransferHandler^ transfer);
	void Upload(String^ localPath, String^ remotePath, TransferFlags transferFlags, unsigned int pipelineSize, TransferHandler^ transfer);
	void Download(String^ remotePath, String^ localPath, TransferFlags transferFlags, TransferHandler^ transfer);
	void Download(String^ remotePath, String^ localPath, TransferFlags transferFlags, unsigned int pipelineSize, TransferHandler^ transfer);
	void Close(ProgressHandler^ progress);

	event SftpVersionEventHandler^	OnSftpVersion;
	event ChannelCloseEventHandler^ OnChannelClose;

internal:
	void Raise_OnSftpVersion(unsigned int version);
	void Raise_OnChannelClose();
	FlowSshCpp::RefPtr<ClientSftpChannelCpp> GetFlowSshCpp();

private:
	ClientSftpChannelCpp* m_flowSshCpp;
	ClientBase^ m_client; // Channel keeps the associated client alive.
};


} // namespace FlowSshNet
} // namespace Bitvise
