#ifndef __FAR_PLUGINS_UTILITIES
#define __FAR_PLUGINS_UTILITIES

/** @mainpage FAR Standard Library
	@author Jouri Mamaev JouriM@uran.ru
	@Eng

	@section intro Introduction
	  C++ library for easy create plugins for file and archive manager FAR. <br>
	  Contains set of classes and functions designed to decrease time for write all types of FAR plugins.

	  Initially created by JouriM@uran.ru

	@section Structure Document structure
	  This documentation structure:
	   - FarStdLib    Full documentation tree;
	   - Consts       Different constants and defines;
	   - Types        Data types;
	   - Functions    Global functions;
	   - Variables    Global variables;

	@section Add Additional helpers.
	  Additional helpers and classes does not relative to FAR API directly but used by other wrappers or
	  incapsulate useful functionality.

	  - FP_Screen - helper for save and restore FAR screen buffer. <br>
					Class for save and restore whole screen buffer.
					Can be used in nested calls. In this case the first call save screen and
					restored after last FP_Screen object destroyed.
	  - FPOpMode  - helper for save panel plugin OpMode value.
	  - SRect     - helper class to incapsulate Win API SMALL_RECT structure with additional public
					methods to manipulate its contents.
*/

/** @defgroup FSTDLib Library Compilation/Porting

	FARStdLibrary already ported to next compilers:
	  - Borland C compiler version 5.xx
	  - Visual C compiler version 6.xx
	  - Symantec  compiler version 7.2
	  - GCC
*/

#define _FAR_USE_FARFINDDATA 1
#include <utils.h>
#include <farplug-mb.h>
using namespace oldfar;
#include <farcolor.h>
#include <farkeys.h>

#ifdef __GNUC__
#pragma pack(1)
#else
#pragma pack(push, 1)
#endif

// --------------------------------------------------------------
#include "funi.h"

// ------------------------------------------------------------------------
#if defined(__FILELOG__)
#define Log(v)  FARINProc::Say v
#define PROC(v) FARINProc _inp v;
#else
#define PROC(v)
#define Log(v)
#endif

// ------------------------------------------------------------------------
/** [fstd_menu.cpp]
	@brief
*/
#if !defined(__FP_NOT_FUNCTIONS__)
struct FMenuItem : public FarMenuItem
{
	FMenuItem(LPCSTR txt, bool sel = false, char ch = 0) { Assign(txt, sel, ch); }
	FMenuItem(const FMenuItem &p) { Assign(p); }
	FMenuItem(void) { Assign(); }

	void Assign(LPCSTR txt, bool sel = false, char ch = 0);
	void Assign(const FMenuItem &p);
	void Assign(void);

	BOOL isChecked(void) { return Checked; }
	void isChecked(BOOL v) { Checked = v; }
	BOOL isSeparator(void) { return Separator; }
	void isSeparator(BOOL v) { Separator = v; }
	BOOL isSelected(void) { return Selected; }
	void isSelected(BOOL v) { Selected = v; }
};

struct FMenuItemEx : public FarMenuItemEx
{
	FMenuItemEx(LPCSTR txt, bool sel = false, char ch = 0) { Assign(txt, sel, ch); }
	FMenuItemEx(const FMenuItemEx &p) { Assign(p); }
	FMenuItemEx(void) { Assign(); }

	void Assign(LPCSTR txt, bool sel = false, char ch = 0);
	void Assign(const FMenuItemEx &p);
	void Assign(void);

	BOOL isChecked(void) { return IS_FLAG(Flags, MIF_CHECKED); }
	void isChecked(BOOL v)
	{
		if (v)
			SET_FLAG(Flags, MIF_CHECKED);
		else
			CLR_FLAG(Flags, MIF_CHECKED);
	}
	BOOL isSeparator(void) { return IS_FLAG(Flags, MIF_SEPARATOR); }
	void isSeparator(BOOL v)
	{
		if (v)
			SET_FLAG(Flags, MIF_SEPARATOR);
		else
			CLR_FLAG(Flags, MIF_SEPARATOR);
	}
	BOOL isSelected(void) { return IS_FLAG(Flags, MIF_SELECTED); }
	void isSelected(BOOL v)
	{
		if (v)
			SET_FLAG(Flags, MIF_SELECTED);
		else
			CLR_FLAG(Flags, MIF_SELECTED);
	}
	BOOL isGrayed(void) { return IS_FLAG(Flags, MIF_DISABLE); }
	void isGrayed(BOOL v)
	{
		if (v)
			SET_FLAG(Flags, MIF_DISABLE);
		else
			CLR_FLAG(Flags, MIF_DISABLE);
	}
};

// ------------------------------------------------------------------------
/** @brief  FP_Menu
	[fstd_menu.cpp]
*/
template <class T>
class FP_MenuTyped
{
	T *List;
	int ItemsCount;
	int MaxCount;

private:
	BOOL Realloc(int DeltaSize);

public:
	FP_MenuTyped(void);
	FP_MenuTyped(LPCSTR strings[], int SelNum = -1, int CheckNum = -1, char CheckChar = 0);
	~FP_MenuTyped() { Free(); }

	T *Add(const T *src, int cn);				// Add a `cn` items to list
	T *Add(const T &p) { return Add(&p, 1); }	// Add 1 item to list
	void Free(void);							// Clear list
	void DeleteAll(void) { Free(); }			// Clear list

	T *Items(void) { return List; }
	int Count(void) { return ItemsCount; }
	T *Item(int num) { return (num >= 0 && num < ItemsCount) ? (List + num) : NULL; }
	T *operator[](int num) { return Item(num); }

	int Execute(LPCSTR Title, DWORD Type = 0, LPCSTR Footer = NULL, LPCSTR Help = NULL,
			const int *BreakKeys = NULL, int *BreakCode = NULL);
};

template <class T>
FP_MenuTyped<T>::FP_MenuTyped(void)
{
	List = NULL;
	ItemsCount = 0;
	MaxCount = 0;
}

template <class T>
FP_MenuTyped<T>::FP_MenuTyped(LPCSTR strings[], int SelNum, int CheckNum, char CheckChar)
{
	int n, cn;
	T *it;

	List = NULL;
	ItemsCount = 0;
	MaxCount = 0;

	if (!strings)
		return;

	for (cn = n = 0; strings[n]; cn++, n++)
		;

	if (!cn)
		return;

	Realloc(cn);

	for (n = 0; n < cn; n++) {
		it = Add(T());
		it->Assign(strings[n], SelNum == n, CheckNum == n ? CheckChar : 0);
	}
}

template <class T>
BOOL FP_MenuTyped<T>::Realloc(int NewSize)
{
	if (!NewSize)
		return FALSE;

	if (NewSize < MaxCount)
		return TRUE;

	MaxCount = NewSize;

	if (!List)
		List = (T *)malloc(sizeof(T) * MaxCount);
	else
		List = (T *)realloc(List, sizeof(T) * MaxCount);

	if (!List)
		return FALSE;

	return TRUE;
}

template <class T>
T *FP_MenuTyped<T>::Add(const T *pi, int icn)
{
	if (!Realloc(ItemsCount + icn))
		return NULL;

	T *p = List + ItemsCount;
	memmove(p, pi, sizeof(*p) * icn);
	ItemsCount+= icn;

	return p;
}

template <class T>
void FP_MenuTyped<T>::Free(void)
{
	if (!ItemsCount)
		return;

	free(List);
	List = NULL;
	ItemsCount = 0;
	MaxCount = 0;
}

template <class T>
int FP_MenuTyped<T>::Execute(LPCSTR Title, DWORD Type, LPCSTR Footer, LPCSTR Help, const int *BreakKeys,
		int *BreakCode)
{
	return FP_Info->Menu(FP_Info->ModuleNumber, -1, -1, 0,
			Type | (sizeof(T) != sizeof(FarMenuItem) ? FMENU_USEEXT : 0), (const char *)FP_GetMsg(Title),
			(const char *)FP_GetMsg(Footer), (const char *)Help, BreakKeys, BreakCode,
			(const struct FarMenuItem *)Items(), Count());
}

typedef FP_MenuTyped<FMenuItem> FP_Menu;
typedef FP_MenuTyped<FMenuItem> *PFP_Menu;

typedef FP_MenuTyped<FMenuItemEx> FP_MenuEx;
typedef FP_MenuTyped<FMenuItemEx> *PFP_MenuEx;

// ------------------------------------------------------------------------
/** @brief FP_Dialog
	[fstd_Dialog.cpp]
*/
struct FP_Dialog
{
	HANDLE Handle;
	int LockCount;

public:
	FP_Dialog(HANDLE h);

	void Assign(HANDLE h) { Handle = h; }

	int Focused(void) const { return (int)FP_Info->SendDlgMessage(Handle, DM_GETFOCUS, 0, 0); }
	void Focused(int num) const { FP_Info->SendDlgMessage(Handle, DM_SETFOCUS, num, 0); }
	int Enabled(int num, int v = -1) const { return (int)FP_Info->SendDlgMessage(Handle, DM_ENABLE, num, v); }
	BOOL Visible(int num, int v = -1) const
	{
		return (BOOL)FP_Info->SendDlgMessage(Handle, DM_SHOWITEM, num, v);
	}

	bool Checked(int num) const
	{
		return FP_Info->SendDlgMessage(Handle, DM_GETCHECK, num, 0) == BSTATE_CHECKED;
	}
	void Checked(int num, bool v) const
	{
		FP_Info->SendDlgMessage(Handle, DM_SETCHECK, num, v ? BSTATE_CHECKED : BSTATE_UNCHECKED);
	}
	void CheckToggle(int num) const { Checked(num, !Checked(num)); }

	bool GetItem(int num, FarDialogItem *p) const
	{
		return FP_Info->SendDlgMessage(Handle, DM_GETDLGITEM, num, (LONG_PTR)p) == TRUE;
	}
	bool SetItem(int num, FarDialogItem *p) const
	{
		return FP_Info->SendDlgMessage(Handle, DM_SETDLGITEM, num, (LONG_PTR)p) == TRUE;
	}

	bool DlgRect(SRect *p) const
	{
		return FP_Info->SendDlgMessage(Handle, DM_GETDLGRECT, 0, (LONG_PTR)p) == TRUE;
	}
	bool ItemRect(int num, SRect *p) const
	{
		return FP_Info->SendDlgMessage(Handle, DM_GETITEMPOSITION, num, (LONG_PTR)p) == TRUE;
	}
	int CursorPos(int num) const
	{
		COORD cp = {0, 0};
		return FP_Info->SendDlgMessage(Handle, DM_GETCURSORPOS, num, (LONG_PTR)&cp) == TRUE ? cp.X : 0;
	}
	bool CursorPos(int num, int pos) const;

	void Lock(bool v = true);
	void Unlock(void) { Lock(false); }
	bool Locked(void) const { return LockCount != 0; }
	void FullUnlock(void);

	void Close(int id = 0) const { FP_Info->SendDlgMessage(Handle, DM_CLOSE, id, 0); }
	void Invalidate(void) const { FP_Info->SendDlgMessage(Handle, DM_REDRAW, 0, 0); }
	void Visible(bool v) const { FP_Info->SendDlgMessage(Handle, DM_SHOWDIALOG, (BOOL)v, 0); }
	LONG_PTR User(int msg, int p = 0, LONG_PTR p1 = 0) const
	{
		return FP_Info->SendDlgMessage(Handle, msg, p, p1);
	}

	int SetText(int num, LPCSTR str, int sz = 0) const;
	LPCSTR GetText(int num) const;
	int GetText(int num, char *buff, int bSz) const;
};
#endif	// !defined(__FP_NOT_FUNCTIONS__)

// ------------------------------------------------------------------------
/** @brief Dynamic array of panel item elements.
	[fstd_ilist.cpp]

	PluginPanelItem Reserved and can be used to store additional data.
	This data will be correctly copied and deleted in FP_ItemList.
	You MUST use _Alloc or strdup to allocate data space.
	Data CAT NOT BE zero sized.
*/
inline BOOL FPIL_ADDEXIST(const PluginPanelItem *p)
{
	return ((p)->Reserved[0] && (p)->Reserved[1]);
}
inline DWORD FPIL_ADDSIZE(const PluginPanelItem *p)
{
	return FPIL_ADDEXIST(p) ? (DWORD)(p)->Reserved[0] : 0;
}
inline LPVOID FPIL_ADDDATA(const PluginPanelItem *p)
{
	return FPIL_ADDEXIST(p) ? ((void *)(p)->Reserved[1]) : NULL;
}
inline void FPIL_ADDSET(PluginPanelItem *p, DWORD sz, LPVOID dt)
{
	(p)->Reserved[0] = sz;
	(p)->Reserved[1] = (DWORD_PTR)dt;
}

struct FP_ItemList
{
	PluginPanelItem *List;	///< Panel items array
	int ItemsCount;			///< Number of items in array
	BOOL needToDelete;		///< Indicate if local copy of items array need to be deleted at destructor
	int MaxCount;			///< Number of elements may be inserted into list without expand list
private:
	BOOL Realloc(int DeltaSize);

public:
	FP_ItemList(BOOL NeedToDelete = TRUE);
	~FP_ItemList() { Clear(); }

	PluginPanelItem *Add(const PluginPanelItem *src, int cn);	///< Add a `cn` items to list
	PluginPanelItem *Add(const PluginPanelItem *src) { return Add(src, 1); }
	void Clear(void);											// Clear list

	PluginPanelItem *Items(void) { return List; }
	int Count(void) { return ItemsCount; }
	PluginPanelItem *Item(int num) { return (num >= 0 && num < ItemsCount) ? (List + num) : NULL; }
	PluginPanelItem *operator[](int num) { return Item(num); }

	static void Free(PluginPanelItem *List, int count);								// Free items, allocated by FP_ItemList
	static void Copy(PluginPanelItem *dest, const PluginPanelItem *src, int cn);	// Copy all data from `src` to `dest`
};

struct FP_SizeItemList : public FP_ItemList
{
	int64_t TotalFullSize;
	int64_t TotalFiles;

public:
	FP_SizeItemList(BOOL NeedToDelete = TRUE)
		:
		FP_ItemList(NeedToDelete)
	{
		TotalFullSize = 0;
		TotalFiles = 0;
	}
};

#ifdef __GNUC__
#pragma pack()
#else
#pragma pack(pop)
#endif

#endif
