#pragma once

// MFC
#define VC_EXTRALEAN
#define WINVER 0x0501		// XP or later
#include <afxwin.h>         // MFC core and standard components
#include <afxext.h>         // MFC extensions
#include <afxcmn.h>			// MFC support for Windows Common Controls
#include <shlobj.h>
#include "resource.h"

// CRT
#include <assert.h>

// std
#include <map>
#include <list>
#include <memory>

// FlowSshCpp
#include "FlowSshCpp.h"


namespace FlowSshCpp_SftpGui
{
using namespace std;
using namespace FlowSshCpp;


// SftpException - notifications

class SftpException : public Exception	
{
public: 
	SftpException (wchar_t const* msg) : Exception(msg) {}
};


// SftpFatalException - application terminates

class SftpFatalException : public SftpException
{ 
public: 
	SftpFatalException (wchar_t const* msg) : SftpException(msg) {}
};


// ConcatPathXX

CStringW ConcatPathLocal(CStringW const& strPathLeft, CStringW const& strPathRight);
CStringW ConcatPathRemote(CStringW const& strPathLeft, CStringW const& strPathRight);
CStringW FormatFileSize(DWORD64 nSizeInBytes);


// FolderSort

struct FolderSort {	enum {
	Acsending	= 0x000,
	Descending  = 0x001,
	Name		= 0x002,
	Ext			= 0x004,
	Size		= 0x008,
	Type		= 0x010,
	CTime		= 0x020, // create time
	Atime		= 0x040, // access time
	MTime		= 0x080, // modify time
	Attrs		= 0x100
}; };


// File

class Folder;
class File
{
public:
	File(Folder* pFolderInfo) : m_pFolderInfo(pFolderInfo) {}	
	virtual ~File() {}

	virtual CStringW GetName() const = 0;
	virtual CStringW GetFullName() const = 0;
	virtual DWORD64  GetSize() const = 0;	
	virtual CStringW GetType() const = 0;	
	virtual CStringW GetAttributes() const = 0;
	
	virtual DWORD64 GetCreateTime() const = 0;
	virtual DWORD64 GetAccessTime() const = 0;
	virtual DWORD64 GetModifyTime() const = 0;

	virtual CStringW GetCreateTimeStr(bool bLongFormat = false) const = 0;
	virtual CStringW GetAccessTimeStr(bool bLongFormat = false) const = 0;
	virtual CStringW GetModifyTimeStr(bool bLongFormat = false) const = 0;

	virtual bool IsFolder() const = 0;
	virtual bool IsFolderSymLink() const = 0; // Is symbolic link to a folder
	virtual bool IsNavigable() const { return IsFolder() || IsFolderSymLink(); }
	virtual bool IsFolderNameCurrentOrParent() const { return (GetName() == L".") || (GetName() == L".."); }

	CStringW GetSizeStr() const { return FormatFileSize(GetSize()); }
	CStringW GetExtension() const;

protected:
	CStringW FormatTime(FILETIME const& fileTime, bool bLongFormat) const;	

	Folder* m_pFolderInfo;
	mutable CStringW m_strExtension;
};


// FileTypeImpl

class FileTypeImpl
{
public:
	FileTypeImpl() {};
	virtual ~FileTypeImpl() {}
	
	std::vector<CStringW>& AccessFileTypeVector() { return m_vecStr2Type; }
	UINT GetFileTypeIdx(CStringW const& str2FileNameOrExtension, DWORD dwFileAttributes = FILE_ATTRIBUTE_NORMAL);
	enum { IdxErr = (UINT)(-1) };

private:	
	struct TypeKey
	{
		CStringW strName;
		DWORD dwFileAttributes;
	};
	struct Data { UINT nIndex; };
	struct TypeKeyLowerThan 
	{
		bool operator()(TypeKey key1, TypeKey key2) const 
		{
			int n = _wcsicmp(key1.strName, key2.strName);
			if (n < 0)
				return true;
			else if (n > 0)
				return false;
			else 
				return key1.dwFileAttributes < key2.dwFileAttributes;
		}
	};	

	typedef map<TypeKey, Data, TypeKeyLowerThan> map_type;
	map_type m_mapType;

	std::vector<CStringW> m_vecStr2Type;	
};


// Folder

class Folder : public FileTypeImpl
{
public:
	Folder(CStringW const& strFolder) : m_strFolder(strFolder), m_nPrimarySortType(FolderSort::Acsending), m_nSecondarySortType(FolderSort::Name) {}
	virtual ~Folder() {}

	virtual UINT	 GetFileCount() const = 0;
	virtual File*	 GetFileInfo(UINT nIdx) const = 0;
	virtual CStringW ConcatPath(CStringW const& strPathRight) const = 0;

	CStringW	GetName() const { return m_strFolder; }
	File*		GetSortedFileInfo(UINT nItem) const { return GetFileInfo(m_vecSortedFileInfoIndex[nItem]); }	
	void		SortFiles(UINT nNewSortType);

protected:
	void SortFiles();

private:
	template <class PrimaryKeyType, class SecondaryKeyType>
	class KeyT
	{
	public:
		KeyT(PrimaryKeyType const& primary, SecondaryKeyType const& secondary, bool bIsNavigable = false)
			: m_primary(primary)
			, m_secondary(secondary)
			, m_bIsNavigable(bIsNavigable) 
		{}

		PrimaryKeyType m_primary;
		SecondaryKeyType m_secondary;
		bool m_bIsNavigable;
	};

	typedef KeyT<CStringW, CStringW> StrStrKey;
	typedef KeyT<DWORD64, CStringW> Word64StrKey;

	class KeyLessThan
	{
	public:
		bool operator()(StrStrKey const& key1, StrStrKey const& key2) const
		{
			if (key1.m_bIsNavigable != key2.m_bIsNavigable)
				return key1.m_bIsNavigable;
			else 
			{
				int nResult = _wcsicmp(key1.m_primary, key2.m_primary);
				if (nResult == 0)
					nResult = _wcsicmp(key1.m_secondary, key2.m_secondary);

				return nResult < 0; 
			}
		}
		bool operator()(Word64StrKey const& key1, Word64StrKey const& key2) const
		{
			if (key1.m_bIsNavigable != key2.m_bIsNavigable)
				return key1.m_bIsNavigable;
			else
			{
				if (key1.m_primary != key2.m_primary)
					return key1.m_primary < key2.m_primary;
				else 
					return _wcsicmp(key1.m_secondary, key2.m_secondary) < 0;
			}
		}
	};

	CStringW m_strFolder;
	UINT m_nPrimarySortType;
	UINT const m_nSecondarySortType;
	std::vector<UINT> m_vecSortedFileInfoIndex;
};



// LocalFile

class LocalFile : public File
{
public:
	LocalFile(WIN32_FIND_DATAW const data, Folder* pFolderInfo) : File(pFolderInfo), m_data(data) {}
	LocalFile(WIN32_FIND_DATAW const data, CStringW const& strFolder) : File(NULL), m_data(data), m_strFolder(strFolder) {}

	CStringW GetName() const { return CStringW(m_data.cFileName); }
	CStringW GetFullName() const;
	DWORD64  GetSize() const { return m_data.nFileSizeHigh*(DWORD64(MAXDWORD)+1) + m_data.nFileSizeLow; }
	CStringW GetType() const;
	CStringW GetAttributes() const;

	DWORD64 GetCreateTime() const { return *((DWORD64*) &m_data.ftCreationTime); }
	DWORD64 GetAccessTime() const { return *((DWORD64*) &m_data.ftLastAccessTime); }
	DWORD64 GetModifyTime() const { return *((DWORD64*) &m_data.ftLastWriteTime); }

	CStringW GetCreateTimeStr(bool bLongFormat = false) const { return FormatTime(m_data.ftCreationTime, bLongFormat); }
	CStringW GetAccessTimeStr(bool bLongFormat = false) const { return FormatTime(m_data.ftLastAccessTime, bLongFormat); }
	CStringW GetModifyTimeStr(bool bLongFormat = false) const { return FormatTime(m_data.ftLastWriteTime, bLongFormat); }	

	bool IsFolder() const { return (m_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; }
	bool IsFolderSymLink() const { return IsFolder() && IsReparsePoint(); }

protected:	
	bool IsReparsePoint() const { return (m_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0; }

private:
	CStringW m_strFolder;
	WIN32_FIND_DATAW m_data;
};


// LocalFolder

class LocalFolder : public Folder
{
public:
	LocalFolder(CStringW const& strFolder);

	UINT	 GetFileCount() const { return (UINT)m_vecFileInfo.size(); }
	File*	 GetFileInfo(UINT nIdx) const { return (File*) &m_vecFileInfo[nIdx]; }	
	CStringW ConcatPath(CStringW const& strPathRight) const { return ConcatPathLocal(GetName(), strPathRight); };

protected:
	std::vector<LocalFile>	m_vecFileInfo;
};


// RemoteFile

class RemoteFile : public File
{
public:	
	RemoteFile(FileInfo const data, Folder* pFolderInfo) : File(pFolderInfo), m_data(data) {}
	RemoteFile(FileInfo const data, CStringW const& strFolder) : File(NULL), m_data(data), m_strFolder(strFolder) {}

	CStringW GetName() const { return CStringW(m_data.m_name.c_str()); }
	CStringW GetFullName() const;
	DWORD64  GetSize() const { return m_data.m_attrs.m_size; }
	CStringW GetType() const;
	CStringW GetAttributes() const;

	DWORD64 GetCreateTime() const { return m_data.m_attrs.m_createTime; }
	DWORD64 GetAccessTime() const { return m_data.m_attrs.m_accessTime; }
	DWORD64 GetModifyTime() const { return m_data.m_attrs.m_modifyTime; }

	CStringW GetCreateTimeStr(bool bLongFormat = false) const { if (!CreateTimeAvailable()) return L"na"; return FormatTime((time_t) GetCreateTime(), bLongFormat); }
	CStringW GetAccessTimeStr(bool bLongFormat = false) const { if (!AccessTimeAvailable()) return L"na"; return FormatTime((time_t) GetAccessTime(), bLongFormat); }
	CStringW GetModifyTimeStr(bool bLongFormat = false) const { if (!ModifyTimeAvailable()) return L"na"; return FormatTime((time_t) GetModifyTime(), bLongFormat); }

	bool IsFolder() const { return m_data.m_attrs.m_type == FlowSshC_FileType_Directory; }
	bool IsFolderSymLink() const { return IsSymLink() && m_data.m_attrs.m_type == FlowSshC_FileType_Directory; }

protected:
	bool IsSymLink() const { return m_data.m_attrs.m_type == FlowSshC_FileType_Directory; }
	bool CreateTimeAvailable() const { return (m_data.m_attrs.m_validAttrFlags & FlowSshC_AttrFlags_CreateTime) != 0; }
	bool AccessTimeAvailable() const { return (m_data.m_attrs.m_validAttrFlags & FlowSshC_AttrFlags_AccessTime) != 0; }
	bool ModifyTimeAvailable() const { return (m_data.m_attrs.m_validAttrFlags & FlowSshC_AttrFlags_ModifyTime) != 0; }
	
	CStringW FormatTime(time_t utcUnixTime, bool bLongFormat) const;

private:
	CStringW m_strFolder;
	FileInfo m_data;
};


// RemoteFolder

class RemoteFolder : public Folder
{
public:
	RemoteFolder(CStringW const& strFolder, HWND hWndParent) : Folder(strFolder) {}
	
	UINT		GetFileCount() const { return (UINT)m_vecFileInfo.size(); }
	File*		GetFileInfo(UINT nIdx) const { return (File*) &m_vecFileInfo[nIdx]; }
	CStringW	ConcatPath(CStringW const& strPathRight) const { return ConcatPathRemote(GetName(), strPathRight); };
	
	void ReserveVectorSize(UINT nSize) { m_vecFileInfo.reserve(nSize); }
	void PushBack(RemoteFile const fileInfo) { m_vecFileInfo.push_back(fileInfo); }

protected:	
	std::vector<RemoteFile> m_vecFileInfo;
};


// FlowSshCpp extensions

struct WinMsg { enum { 
	ClientHostKey	= WM_USER + 0x1000,
	ClientFurtherAuth,
	ClientDisconnected,
	ClientConnected,	
	SftpOpened,
	SftpRealPath,
	SftpList,
	SftpListTransfer,
	SftpTransfer,
	SftpTransferDone,	// Upload & Download
	SftpListErase,
	SftpRemove,
	SftpRmDir,
	SftpStat,
	SftpSetStat,
	SftpRename,
	SftpMkDir
}; };


// MyErrorHandler

class MyErrorHandler : public ErrorHandler
{
protected:
	virtual void OnExceptionInHandler(bool fatal, wchar_t const* desc)
	{
		static volatile long alreadyCalled = 0;
		if (InterlockedIncrement(&alreadyCalled) != 1) return;

		// close main window
		CWinApp* app = AfxGetApp();
		if (!app)
			assert("AfxGetApp failed.");
		else if (app->m_pMainWnd) // AfxGetMainWnd is broken in VS 2005
			PostMessageW(app->m_pMainWnd->GetSafeHwnd(), WM_CLOSE, NULL, NULL);
		
		MessageBoxW(NULL, desc, fatal ? L"OnExceptionInHandler - Error [fatal]" : L"OnExceptionInHandler - Error", MB_OK);
	}

private:
	~MyErrorHandler() {}	// This object must be created on the heap.
};


// MyClient

class MyClient : public FlowSshCpp::Client
{
public:
	MyClient(HWND hWnd) : m_hWnd(hWnd) 
	{
		// CHANGE APPLICATION NAME IN PRODUCTION CODE!
		SetAppName(L"FlowSshCpp_SftpGui 1.0");
	}

protected:
	bool OnHostKey(RefPtr<PublicKey> publicKey)
	{
		if (IsWindow(m_hWnd))
			return SendMessageW(m_hWnd, WinMsg::ClientHostKey, WPARAM(publicKey.Get()), 0) != 0;
		return false;	// host key rejected
	}	

	bool OnFurtherAuth(FurtherAuth& furtherAuth)
	{
		if (IsWindow(m_hWnd))
			return SendMessageW(m_hWnd, WinMsg::ClientFurtherAuth, WPARAM(&furtherAuth), 0) != 0;
		return false;
	}

	void OnDisconnect(UINT reason, std::wstring const& desc) 
	{
		if (IsWindow(m_hWnd))
			SendMessageW(m_hWnd, WinMsg::ClientDisconnected, WPARAM(reason), LPARAM(desc.c_str()));
	}

private:
	~MyClient() {}	// This object must be created on the heap.
	HWND m_hWnd;
};


// MyListMsg

class MyListMsg : public ListMsg
{
public:
	MyListMsg(HWND hWnd, UINT nMsg) : ListMsg(hWnd, nMsg) {}
	std::unique_ptr<RemoteFolder> m_pRemoteFolderInfo;

protected:
	bool OnList(std::vector<FileInfo> const& fileInfos, bool endOfList)
	{
		size_t nrFileInfos = fileInfos.size();
		m_pRemoteFolderInfo->ReserveVectorSize((UINT) (m_pRemoteFolderInfo->GetFileCount() + nrFileInfos));
		for (size_t c = 0; c < nrFileInfos; c++)
		{
			RemoteFile fileInfo(fileInfos[c], m_pRemoteFolderInfo.get());
			if (!fileInfo.IsFolderNameCurrentOrParent())
				m_pRemoteFolderInfo->PushBack(fileInfo);
		}
		return true;
	}

private:
	~MyListMsg() {}	// This object must be created on the heap.
};


// ListTransferMsg

class ListTransferMsg : public ListMsg
{
public:
	ListTransferMsg(HWND hWnd, UINT nMsg) : ListMsg(hWnd, nMsg) {}
	CStringW m_strFolder;
	CStringW m_strFolderDest;

private:
	~ListTransferMsg() {}	// This object must be created on the heap.
};


// ListEraseMsg

class ListEraseMsg : public ListMsg
{
public:
	ListEraseMsg(HWND hWnd, UINT nMsg) : ListMsg(hWnd, nMsg) {}
	CStringW m_strFolder;
	vector<CStringW> m_vecFiles;
	vector<CStringW> m_vecFolders;

protected:
	bool OnList(std::vector<FileInfo> const& fileInfos, bool endOfList)
	{
		size_t nrFileInfos = fileInfos.size();
		for (size_t c=0; c < nrFileInfos; c++)
		{
			RemoteFile fileInfo(fileInfos[c], m_strFolder);
			if (!fileInfo.IsFolderNameCurrentOrParent())
				if (!fileInfo.IsNavigable()) m_vecFiles.push_back(fileInfo.GetFullName());
				else						 m_vecFolders.push_back(fileInfo.GetFullName());
		}
		return true;
	}

private:
	~ListEraseMsg() {}	// This object must be created on the heap.
};


// MyTransferMsg

class MyTransferMsg : public TransferMsg
{
public:
	MyTransferMsg(HWND hWnd, UINT nMsgTransfer, UINT nMsgDone, UINT nRequestId) 
		: TransferMsg(hWnd, nMsgDone, nRequestId), m_bXfering(TRUE), m_nMsgTransfer(nMsgTransfer)
		{ EnsureThrow(nRequestId <= INT_MAX); }
	bool m_bXfering;

protected:
	bool OnTransfer(bool done, FlowSshC_TransferStat const& transferStat)
	{
		if (IsWindow(m_wnd))
		{
			m_bXfering = ::SendMessageW(m_wnd, m_nMsgTransfer, WPARAM(&transferStat), LPARAM(m_requestId)) != 0;
			return m_bXfering;
		}
		m_bXfering = false;
		return m_bXfering;
	}

private:
	~MyTransferMsg() {}	// This object must be created on the heap.
	UINT m_nMsgTransfer;	
};


// MyStatMsg

class MyStatMsg : public StatMsg
{
public:
	MyStatMsg(HWND hWnd, UINT nMsg) : StatMsg(hWnd, nMsg) {}
	CStringW m_strFile;
	CStringW m_strFileType;
	CStringW m_strFolder;

private:	
	~MyStatMsg() {}	// This object must be created on the heap.
};



// MFC derived classes --------
// FolderListCtrl

class FolderListCtrl : public CListCtrl
{
public:
	FolderListCtrl() : m_nFolderSortType(FolderSort::Name), m_pFolderInfo(nullptr) {}

	int		 InsertColumn(int nColumn, UINT folderSort, LPCWSTR lpszColumnHeading, int nFormat, int nWidth);	
	void	 SetFolderInfo(std::unique_ptr<Folder>&& pNewFolderInfo);
	void	 Display();	

	CStringW GetFolderName() const { if (m_pFolderInfo.get()) return m_pFolderInfo->GetName(); return L""; }

	std::vector<UINT> GetSelectedItems() const;
	File*	 GetSortedFileInfo(UINT nItem) const { return m_pFolderInfo->GetSortedFileInfo(nItem); }	
	void	 Refresh() { SetFolder(GetFolderName()); }

	virtual void SetFolder(CStringW const& folerName) = 0;
	virtual void GoUp() = 0;	

protected:
	void SortFiles(UINT folderSort, bool bAscending);
	afx_msg void OnColumnClick(NMHDR* pNMHDR, LRESULT* pResult);
	afx_msg void OnListDoubleClick(NMHDR* pNMHDR, LRESULT* pResult);
	DECLARE_MESSAGE_MAP()

protected:
	int InsertItem(const LVITEM*) { return 0; }
	int InsertItem(int, LPCWSTR) { return 0; }
	int InsertItem(int, LPCWSTR, int) { return 0; }	

	UINT m_nFolderSortType;
	std::vector<UINT> m_vecFolderSortHeader;
	std::unique_ptr<Folder> m_pFolderInfo;
};


// LocalFolderListCtrl

class LocalFolderListCtrl : public FolderListCtrl
{
public:	
	void SetFolder(CStringW const& folerName);
	void GoUp();
};


// RemoteFolderListCtrl

class RemoteFolderListCtrl : public FolderListCtrl
{
public:
	void SetFolder(CStringW const& folerName);
	void GoUp();
};


// TransferListCtrl

class TransferListCtrl  : public CListCtrl
{
public:
	void DeleteSelectedItems();
	void DeleteCompletedItems();
};


// UserAuthDialog

class UserAuthDialog : public CDialog
{
public:
	enum { IDD = DLG_USER_AUTH };
	UserAuthDialog(Client* client, CWnd* pParent = NULL);
	UserAuthDialog(Client::FurtherAuth* furtherAuth, CWnd* pParent = NULL);

protected:
	void SetCtrlSelection(BOOL bPasswordCtrls);

	virtual void DoDataExchange(CDataExchange* pDX);
	virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
	virtual BOOL OnInitDialog();
	virtual void OnOK();

	afx_msg void OnBnClickedSelChanged(UINT) { SetCtrlSelection(m_btnUsePassword.GetCheck()); }
	afx_msg void OnBnClickedBtnFileDialog();
	DECLARE_MESSAGE_MAP()

private:
	Client::FurtherAuth* m_furtherAuth;
	Client* m_client;

	CButton  m_btnUsePassword;
	CButton	 m_btnUsePublicKey;

	CEdit	 m_edtPassword;
	CStringW m_strPassword;

	CEdit	 m_edtPassphrase;
	CStringW m_strPassphrase;
	CEdit	 m_edtKeyFile;
	CStringW m_strKeyFile;
	CButton  m_btnFileDialog;
};


// FolderNameDialog

class FolderNameDialog : public CDialog
{
public:
	enum { IDD = DLG_FOLDER_NAME };
	FolderNameDialog(CWnd* pParent = NULL);
	CStringW GetNewFolder() { return m_strNewFolder; }

protected:
	virtual void DoDataExchange(CDataExchange* pDX);
	DECLARE_MESSAGE_MAP()

private:
	CStringW m_strNewFolder;
};


// RenameFileDialog

class RenameFileDialog : public CDialog
{
public:
	enum { IDD = DLG_RENAME_FILE };
	RenameFileDialog(CStringW const& strOldName, CWnd* pParent = NULL);
	CStringW GetOldName() { return m_strOldName; }
	CStringW GetNewName() { return m_strNewName; }

protected:
	virtual void DoDataExchange(CDataExchange* pDX);
	DECLARE_MESSAGE_MAP()

private:
	CStringW m_strOldName;
	CStringW m_strNewName;
};


// FilePropertiesDialog

class FilePropertiesDialog : public CDialog
{
public:
	enum { IDD = DLG_FILE_PROPERTIES };
	FilePropertiesDialog(MyStatMsg const* pStat, CWnd* pParent = NULL);
	FileAttrs GetFileAttrs() { return m_fileAttrs; }
	BOOL FileAttrsChanged() { return m_bFileAttrsChanged; }

protected:
	virtual void DoDataExchange(CDataExchange* pDX);
	virtual void OnOK();
	DECLARE_MESSAGE_MAP()

private:
	BOOL m_bFileAttrsChanged;
	FileAttrs m_fileAttrs;

	CStringW m_strName;
	CStringW m_strType;
	CStringW m_strLocation;
	CStringW m_strSize;
	CStringW m_strCreated;
	CStringW m_strModified;
	CStringW m_strAcessed;
	CStringW m_strOwner;
	CStringW m_strGroup;
	CStringW m_strAttributes;
};


// MyToolBarCtrl

struct WinCmd { enum {
	GoUpLocal = 0x500,
	RefreshLocal,
	GoUp,
	Refresh,
	Upload,
	Download,
	Erase,
	Stat,
	Rename,
	MkDir,
	RemoveSelected,
	RemoveCompleted,
	RemoveAll,
	Pause
}; };

class MyToolBarCtrl : public CToolBarCtrl
{
public:
	virtual BOOL Create(BOOL bList, CWnd* pParentWnd, UINT nID);
	void AddSeparator();
	void AddButton(int idxImage,int idCommand, char* pString = NULL, BYTE fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE);
};


// TransferImpl

class MainDialog;
class TransferImpl
{
public:
	static const int nMaxTransfers = 5;
	TransferImpl() : m_bListingOk(TRUE), m_bFilesListed(TRUE), m_nOpenListRequests(0), m_nOpenTransferRequests(0), 
					 m_bTransfersEnabled(TRUE), m_pMainDialog(NULL), m_pLstTransfer(NULL) {}	
	void Init(MainDialog* pMainDialog, TransferListCtrl* pLstTransfer, RefPtr<ClientSftpChannel> sftpChannel);

	void UploadSelectedLstItems(LocalFolderListCtrl& lstFolderLocal);
	void DownloadSelectedLstItems(RemoteFolderListCtrl& lstFolderRemote);
	void EnableTransfers(BOOL bEnable = TRUE);

protected:
	// helpers
	void	 ListLocalFiles(CStringW const& strFolder, CStringW const& strFolderDest);
	void	 AddToListCtrl(bool bUpload, PCWSTR strFile, PCWSTR strSourceFolder, PCWSTR strDestinationFolder, PCWSTR strSize = L"0");
	int		 RequestIdToListCtrlItemId(UINT nRequestId);	// return "-1" if request was removed from m_pLstTransfer
	CStringW FormatTransferErr(TransferErr const& err);	
	CStringW UInt2Str(DWORD64 value) { CStringW str; str.Format(L"%02d", value); return str; }
	CStringW FormatTimer(DWORD64 nElapsedSeconds);

	// main implementation & message handlers
	void OnFilesListed();		// used for remote SftpListing (never for ListLocalFiles)
	void OnFilesTransfered(BOOL bRefreshLst);
	void TiggerTransfers(BOOL bRefreshLstIfDone = TRUE);

	void SftpListTransfer(CStringW const& strFolder, CStringW const& strFolderLocal);
	void SftpTransfer(UINT nRequestId);

	afx_msg LRESULT OnSftpListTransfer(ListTransferMsg const& list);
	afx_msg LRESULT OnSftpTransfer(FlowSshC_TransferStat const& transferStat, UINT nRequestId);
	afx_msg LRESULT OnSftpTransferDone(MyTransferMsg const& transfer, UINT nRequestId);	

private:
	BOOL m_bListingOk;	
	BOOL m_bFilesListed;
	int  m_nOpenListRequests;
	int  m_nOpenTransferRequests;
	BOOL m_bTransfersEnabled;
	std::list<UINT> m_vecRequestId;

	MainDialog*		  m_pMainDialog;
	TransferListCtrl* m_pLstTransfer;
	RefPtr<ClientSftpChannel> m_sftpChannel;
	
};


// EraseImpl - recursively deletes all files & folders selected in RemoteFolderListCtrl

class EraseImpl
{
public:
	EraseImpl() : m_bOK(TRUE), m_bEraseDone(TRUE), m_nOpenRequests(0), m_pMainDialog(0) {}	
	void Init(MainDialog* pMainDialog, RefPtr<ClientSftpChannel> sftpChannel);
	void EraseSelectedLstItems(RemoteFolderListCtrl& lstFolderRemote);

protected:
	bool NotificationMsgBox();
	void EraseAllFiles();
	void EraseAllFolders();

	void OnFilesListed();
	void OnFilesErased();
	void OnFoldersErased();	
	void OnEraseDone(BOOL bRefreshLst = TRUE);

	void SftpListErase(CStringW const& strFolder);
	void SftpRemove(CStringW const& strFile);
	void SftpRmDir(CStringW const& strFolder);

	afx_msg LRESULT OnSftpListErase(ListEraseMsg const& list);
	afx_msg LRESULT OnSftpRemove(SftpMsg const& response);
	afx_msg LRESULT OnSftpRmDir(SftpMsg const& response);

private:
	BOOL m_bOK;
	BOOL m_bEraseDone;
	int  m_nOpenRequests;
	vector<CStringW> m_vecFiles;
	vector<CStringW> m_vecFolders;

	MainDialog* m_pMainDialog;
	RefPtr<ClientSftpChannel> m_sftpChannel;
};


// MainDialog

struct DlgState { enum {
	Disconnected = 0,
	Connecting,
	Disconnecting,
	Connected,	
	SftpAction,
	SftpActionDone
}; };

class MainDialog : public CDialog, public TransferImpl, public EraseImpl
{
public:
	enum { IDD = DLG_FLOWSSHCPP_SFTP };
	MainDialog(CWnd* pParent = NULL);
	
	BOOL IsConnected() { return (m_nState >= DlgState::Connected); }
	void SetState(UINT nState);
	void SetLocalFolder(CStringW const& strFolder);
	void SetRemoteFolder(CStringW const& strFolder);
	UINT GetPipelineSize() const { return m_nPipelineSizeKb*1024; }

protected:
	void ShowListErr(wchar_t const* strCaption, ListErr const& listErr);
	void ShowSftpErr(wchar_t const* strCaption, SftpErr const& sftpErr);

	virtual void DoDataExchange(CDataExchange* pDX);
	virtual void OnOK();
	virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);	
	virtual BOOL OnInitDialog();
	virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);

	afx_msg void OnBnClickedBtnConnect();
	afx_msg void OnEnKillFocusPipelineSizeKb();
	afx_msg LRESULT OnClientHostKey(WPARAM, LPARAM);
	afx_msg LRESULT OnClientFurtherAuth(WPARAM, LPARAM);
	afx_msg LRESULT OnClientDisconnected(WPARAM, LPARAM);
	afx_msg LRESULT OnClientConnected(WPARAM, LPARAM);
	afx_msg LRESULT OnSftpOpened(WPARAM, LPARAM);
	afx_msg LRESULT OnSftpRealPath(WPARAM, LPARAM);
	afx_msg LRESULT OnSftpList(WPARAM, LPARAM);
	afx_msg LRESULT OnSftpListTransfer(WPARAM, LPARAM);
	afx_msg LRESULT OnSftpTransfer(WPARAM, LPARAM);
	afx_msg LRESULT OnSftpTransferDone(WPARAM, LPARAM);
	afx_msg LRESULT OnSftpListErase(WPARAM, LPARAM);
	afx_msg LRESULT OnSftpRemove(WPARAM, LPARAM);
	afx_msg LRESULT OnSftpRmDir(WPARAM, LPARAM);
	afx_msg LRESULT OnSftpStat(WPARAM, LPARAM);
	afx_msg LRESULT OnSftpSetStat(WPARAM, LPARAM);
	afx_msg LRESULT OnSftpRename(WPARAM, LPARAM);
	afx_msg LRESULT OnSftpMkDir(WPARAM, LPARAM);
	DECLARE_MESSAGE_MAP()
	
private:
	// DDX controls	
	CStringW		m_strHost;
	CEdit			m_edtHost;
	UINT			m_nPort;
	CEdit			m_edtPort;
	CStringW		m_strUser;
	CEdit			m_edtUser;
	CButton			m_btnConnect;
	CStringW		m_strFolderLocal;
	CStringW		m_strFolderRemote;
	CEdit			m_edtFolderRemote;
	LocalFolderListCtrl	 m_lstFolderLocal;
	RemoteFolderListCtrl m_lstFolderRemote;
	TransferListCtrl	 m_lstTransfer;
	CEdit			m_edtPipelineSizeKb;
	UINT			m_nPipelineSizeKb;

	CImageList		m_imageList;
	MyToolBarCtrl	m_tbcNavLocal;
	MyToolBarCtrl	m_tbcNavRemote;
	MyToolBarCtrl	m_tbcActionLocal;
	MyToolBarCtrl	m_tbcActionRemote;
	MyToolBarCtrl	m_tbcActionTransfer;
	
	UINT					  m_nState;
	RefPtr<MyClient>		  m_client;
	RefPtr<ClientSftpChannel> m_sftpChannel;

	friend class TransferImpl;
	friend class EraseImpl;
};


// SftpApp

class SftpApp : public CWinApp
{
public:
	SftpApp() {};
	virtual BOOL InitInstance();
	DECLARE_MESSAGE_MAP()
};


// Manifest

#ifdef _UNICODE
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif


} // namespace FlowSshCpp_SftpGui