//	Altirra - Atari 800/800XL/5200 emulator
//	UI library
//	Copyright (C) 2009-2012 Avery Lee
//
//	This program is free software; you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation; either version 2 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program; if not, write to the Free Software
//	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

#ifndef f_AT_ATUI_UIPROXIES_H
#define f_AT_ATUI_UIPROXIES_H

#include <vd2/system/event.h>
#include <vd2/system/function.h>
#include <vd2/system/refcount.h>
#include <vd2/system/unknown.h>
#include <vd2/system/vdstl.h>
#include <vd2/system/vectors.h>
#include <vd2/system/VDString.h>
#include <vd2/system/win32/miniwindows.h>

#include <vd2/system/function.h>

struct VDUIAccelerator;
class VDFunctionThunk;

class VDUIProxyControl : public vdlist_node {
public:
	VDUIProxyControl();

	VDZHWND GetHandle() const { return mhwnd; }

	virtual void Attach(VDZHWND hwnd);
	virtual void Detach();

	vdrect32 GetArea() const;
	void SetArea(const vdrect32& r);

	void SetEnabled(bool);
	void SetRedraw(bool);

	virtual VDZLRESULT On_WM_COMMAND(VDZWPARAM wParam, VDZLPARAM lParam);
	virtual VDZLRESULT On_WM_NOTIFY(VDZWPARAM wParam, VDZLPARAM lParam);

protected:
	VDZHWND	mhwnd;
	int mRedrawInhibitCount;
};

class VDUIProxyMessageDispatcherW32 {
public:
	void AddControl(VDUIProxyControl *control);
	void RemoveControl(VDZHWND hwnd);
	void RemoveAllControls(bool detach);

	VDZLRESULT Dispatch_WM_COMMAND(VDZWPARAM wParam, VDZLPARAM lParam);
	VDZLRESULT Dispatch_WM_NOTIFY(VDZWPARAM wParam, VDZLPARAM lParam);

protected:
	size_t Hash(VDZHWND hwnd) const;
	VDUIProxyControl *GetControl(VDZHWND hwnd);

	enum { kHashTableSize = 31 };

	typedef vdlist<VDUIProxyControl> HashChain;
	HashChain mHashTable[kHashTableSize];
};

/////////////////////////////////////////////////////////////////////////////

class IVDUIListViewVirtualItem : public IVDRefCount {
public:
	virtual void GetText(int subItem, VDStringW& s) const = 0;
};

class IVDUIListViewVirtualComparer {
public:
	virtual int Compare(IVDUIListViewVirtualItem *x, IVDUIListViewVirtualItem *y) = 0;
};

class VDUIProxyListView : public VDUIProxyControl {
public:
	VDUIProxyListView();

	void AutoSizeColumns(bool expandlast = false);
	void Clear();
	void ClearExtraColumns();
	void DeleteItem(int index);
	int GetColumnCount() const;
	int GetItemCount() const;
	int GetSelectedIndex() const;
	void SetSelectedIndex(int index);
	IVDUIListViewVirtualItem *GetSelectedItem() const;
	void GetSelectedIndices(vdfastvector<int>& indices) const;
	void SetFullRowSelectEnabled(bool enabled);
	void SetGridLinesEnabled(bool enabled);
	void SetItemCheckboxesEnabled(bool enabled);
	void EnsureItemVisible(int index);
	int GetVisibleTopIndex();
	void SetVisibleTopIndex(int index);
	IVDUIListViewVirtualItem *GetSelectedVirtualItem() const;
	IVDUIListViewVirtualItem *GetVirtualItem(int index) const;
	void InsertColumn(int index, const wchar_t *label, int width, bool rightAligned = false);
	int InsertItem(int item, const wchar_t *text);
	int InsertVirtualItem(int item, IVDUIListViewVirtualItem *lvvi);
	void RefreshItem(int item);
	void RefreshAllItems();
	void EditItemLabel(int item);
	void GetItemText(int item, VDStringW& s) const;
	void SetItemText(int item, int subitem, const wchar_t *text);

	bool IsItemChecked(int item);
	void SetItemChecked(int item, bool checked);
	void SetItemCheckedVisible(int item, bool visible);

	void SetItemImage(int item, uint32 imageIndex);

	bool GetItemScreenRect(int item, vdrect32& r) const;

	void Sort(IVDUIListViewVirtualComparer& comparer);

	VDEvent<VDUIProxyListView, int>& OnColumnClicked() {
		return mEventColumnClicked;
	}

	VDEvent<VDUIProxyListView, int>& OnItemSelectionChanged() {
		return mEventItemSelectionChanged;
	}

	VDEvent<VDUIProxyListView, int>& OnItemDoubleClicked() {
		return mEventItemDoubleClicked;
	}

	struct ContextMenuEvent {
		int mIndex;
		int mX;
		int mY;
	};

	VDEvent<VDUIProxyListView, ContextMenuEvent>& OnItemContextMenu() {
		return mEventItemContextMenu;
	}

	struct CheckedChangingEvent {
		int mIndex;
		bool mbNewVisible;
		bool mbNewChecked;
		bool mbAllowChange;
	};

	VDEvent<VDUIProxyListView, CheckedChangingEvent *>& OnItemCheckedChanging() {
		return mEventItemCheckedChanging;
	}

	VDEvent<VDUIProxyListView, int>& OnItemCheckedChanged() {
		return mEventItemCheckedChanged;
	}

	struct LabelChangedEvent {
		bool mbAllowEdit;
		int mIndex;
		const wchar_t *mpNewLabel;
	};

	VDEvent<VDUIProxyListView, LabelChangedEvent *>& OnItemLabelChanged() {
		return mEventItemLabelEdited;
	}

	VDEvent<VDUIProxyListView, int>& OnItemBeginDrag() {
		return mEventItemBeginDrag;
	}

	VDEvent<VDUIProxyListView, int>& OnItemBeginRDrag() {
		return mEventItemBeginRDrag;
	}

protected:
	void Detach();

	static int VDZCALLBACK SortAdapter(VDZLPARAM x, VDZLPARAM y, VDZLPARAM cookie);
	VDZLRESULT On_WM_NOTIFY(VDZWPARAM wParam, VDZLPARAM lParam);

	int			mChangeNotificationLocks;
	int			mNextTextIndex;
	VDStringW	mTextW[3];
	VDStringA	mTextA[3];

	vdfastvector<int>	mColumnWidthCache;

	VDEvent<VDUIProxyListView, int> mEventColumnClicked;
	VDEvent<VDUIProxyListView, int> mEventItemSelectionChanged;
	VDEvent<VDUIProxyListView, int> mEventItemDoubleClicked;
	VDEvent<VDUIProxyListView, int> mEventItemCheckedChanged;
	VDEvent<VDUIProxyListView, CheckedChangingEvent *> mEventItemCheckedChanging;
	VDEvent<VDUIProxyListView, ContextMenuEvent> mEventItemContextMenu;
	VDEvent<VDUIProxyListView, LabelChangedEvent *> mEventItemLabelEdited;
	VDEvent<VDUIProxyListView, int> mEventItemBeginDrag;
	VDEvent<VDUIProxyListView, int> mEventItemBeginRDrag;
};

/////////////////////////////////////////////////////////////////////////////

class VDUIProxyHotKeyControl : public VDUIProxyControl {
public:
	VDUIProxyHotKeyControl();
	~VDUIProxyHotKeyControl();

	bool GetAccelerator(VDUIAccelerator& accel) const;
	void SetAccelerator(const VDUIAccelerator& accel);

	VDEvent<VDUIProxyHotKeyControl, VDUIAccelerator>& OnEventHotKeyChanged() {
		return mEventHotKeyChanged;
	}

protected:
	VDZLRESULT On_WM_COMMAND(VDZWPARAM wParam, VDZLPARAM lParam);

	VDEvent<VDUIProxyHotKeyControl, VDUIAccelerator> mEventHotKeyChanged;
};

/////////////////////////////////////////////////////////////////////////////

class VDUIProxyTabControl : public VDUIProxyControl {
public:
	VDUIProxyTabControl();
	~VDUIProxyTabControl();

	void AddItem(const wchar_t *s);
	void DeleteItem(int index);

	vdsize32 GetControlSizeForContent(const vdsize32&) const;
	vdrect32 GetContentArea() const;

	int GetSelection() const;
	void SetSelection(int index);

	VDEvent<VDUIProxyTabControl, int>& OnSelectionChanged() {
		return mSelectionChanged;
	}

protected:
	VDZLRESULT On_WM_NOTIFY(VDZWPARAM wParam, VDZLPARAM lParam);

	VDEvent<VDUIProxyTabControl, int> mSelectionChanged;
};

/////////////////////////////////////////////////////////////////////////////

class VDUIProxyListBoxControl final : public VDUIProxyControl {
public:
	VDUIProxyListBoxControl();
	~VDUIProxyListBoxControl();

	void EnableAutoItemEditing();

	void Clear();
	int AddItem(const wchar_t *s, uintptr data = 0);
	int InsertItem(int pos, const wchar_t *s, uintptr data = 0);
	void DeleteItem(int pos);
	void EnsureItemVisible(int pos);
	void EditItem(int pos);

	void SetItemText(int index, const wchar_t *s);
	uintptr GetItemData(int index) const;

	int GetSelection() const;
	void SetSelection(int index);

	void MakeSelectionVisible();

	void SetTabStops(const int *units, uint32 n);

	void SetOnSelectionChanged(vdfunction<void(int)> fn);
	void SetOnItemDoubleClicked(vdfunction<void(int)> fn);
	void SetOnItemEdited(vdfunction<void(int ,const wchar_t *)> fn);

	VDEvent<VDUIProxyListBoxControl, int>& OnSelectionChanged() {
		return mSelectionChanged;
	}

	VDEvent<VDUIProxyListBoxControl, int>& OnItemDoubleClicked() {
		return mEventItemDoubleClicked;
	}

protected:
	void EndEditItem();
	void CancelEditTimer();
	
	void Detach() override;

	VDZLRESULT On_WM_COMMAND(VDZWPARAM wParam, VDZLPARAM lParam);
	VDZLRESULT ListBoxWndProc(VDZHWND hwnd, VDZUINT msg, VDZWPARAM wParam, VDZLPARAM lParam);
	VDZLRESULT LabelEditWndProc(VDZHWND hwnd, VDZUINT msg, VDZWPARAM wParam, VDZLPARAM lParam);
	void AutoEditTimerProc(VDZHWND, VDZUINT, VDZUINT_PTR, VDZDWORD);

	uint32 mSuppressNotificationCount = 0;

	int mEditItem = -1;
	VDZHWND mhwndEdit = nullptr;
	void (*mPrevEditWndProc)() = nullptr;
	VDFunctionThunk *mpEditWndProcThunk = nullptr;
	
	void (*mPrevWndProc)() = nullptr;
	VDFunctionThunk *mpWndProcThunk = nullptr;
	VDFunctionThunk *mpEditTimerThunk = nullptr;
	VDZUINT_PTR mAutoEditTimer = 0;

	vdfunction<void(int)> mpFnSelectionChanged;
	vdfunction<void(int)> mpFnItemDoubleClicked;
	vdfunction<void(int, const wchar_t *)> mpFnItemEdited;
	VDEvent<VDUIProxyListBoxControl, int> mSelectionChanged;
	VDEvent<VDUIProxyListBoxControl, int> mEventItemDoubleClicked;
};

/////////////////////////////////////////////////////////////////////////////

class VDUIProxyComboBoxControl : public VDUIProxyControl {
public:
	VDUIProxyComboBoxControl();
	~VDUIProxyComboBoxControl();

	void AddItem(const wchar_t *s);

	int GetSelection() const;
	void SetSelection(int index);

	VDEvent<VDUIProxyComboBoxControl, int>& OnSelectionChanged() {
		return mSelectionChanged;
	}

protected:
	VDZLRESULT On_WM_COMMAND(VDZWPARAM wParam, VDZLPARAM lParam);

	VDEvent<VDUIProxyComboBoxControl, int> mSelectionChanged;
};

/////////////////////////////////////////////////////////////////////////////

class IVDUITreeViewVirtualItem : public IVDRefUnknown {
public:
	virtual void GetText(VDStringW& s) const = 0;
};

class IVDUITreeViewVirtualItemComparer {
public:
	virtual int Compare(IVDUITreeViewVirtualItem& x, IVDUITreeViewVirtualItem& y) const = 0;
};

class VDUIProxyTreeViewControl final : public VDUIProxyControl {
public:
	typedef uintptr NodeRef;

	static const NodeRef kNodeRoot;
	static const NodeRef kNodeFirst;
	static const NodeRef kNodeLast;

	VDUIProxyTreeViewControl();
	~VDUIProxyTreeViewControl();

	virtual void Attach(VDZHWND hwnd);
	virtual void Detach();

	IVDUITreeViewVirtualItem *GetSelectedVirtualItem() const;

	template<class T>
	T *GetSelectedVirtualItem() const { return static_cast<T *>(GetSelectedVirtualItem()); }

	IVDUITreeViewVirtualItem *GetVirtualItem(NodeRef ref) const;

	void Clear();
	void DeleteItem(NodeRef ref);
	NodeRef AddItem(NodeRef parent, NodeRef insertAfter, const wchar_t *label);
	NodeRef AddVirtualItem(NodeRef parent, NodeRef insertAfter, IVDUITreeViewVirtualItem *item);

	void MakeNodeVisible(NodeRef node);
	void SelectNode(NodeRef node);
	void RefreshNode(NodeRef node);
	void ExpandNode(NodeRef node, bool expanded);
	void EditNodeLabel(NodeRef node);
	bool HasChildren(NodeRef parent) const;
	void EnumChildren(NodeRef parent, const vdfunction<void(IVDUITreeViewVirtualItem *)>& callback);
	void EnumChildrenRecursive(NodeRef parent, const vdfunction<void(IVDUITreeViewVirtualItem *)>& callback);
	void SortChildren(NodeRef parent, IVDUITreeViewVirtualItemComparer& comparer);

	VDEvent<VDUIProxyTreeViewControl, int>& OnItemSelectionChanged() {
		return mEventItemSelectionChanged;
	}

	VDEvent<VDUIProxyTreeViewControl, bool *>& OnItemDoubleClicked() {
		return mEventItemDoubleClicked;
	}

	struct BeginEditEvent {
		NodeRef mNode;
		IVDUITreeViewVirtualItem *mpItem;
		bool mbAllowEdit;
		bool mbOverrideText;
		VDStringW mOverrideText;
	};

	VDEvent<VDUIProxyTreeViewControl, BeginEditEvent *>& OnItemBeginEdit() {
		return mEventItemBeginEdit;
	}

	struct EndEditEvent {
		NodeRef mNode;
		IVDUITreeViewVirtualItem *mpItem;
		const wchar_t *mpNewText;
	};

	VDEvent<VDUIProxyTreeViewControl, EndEditEvent *>& OnItemEndEdit() {
		return mEventItemEndEdit;
	}

	struct GetDispAttrEvent {
		IVDUITreeViewVirtualItem *mpItem;
		bool mbIsBold;
		bool mbIsMuted;
	};

	VDEvent<VDUIProxyTreeViewControl, GetDispAttrEvent *>& OnItemGetDisplayAttributes() {
		return mEventItemGetDisplayAttributes;
	}

	struct BeginDragEvent {
		NodeRef mNode;
		IVDUITreeViewVirtualItem *mpItem;
		vdpoint32 mPos;
	};

	void SetOnBeginDrag(const vdfunction<void(const BeginDragEvent& event)>& fn);
	NodeRef FindDropTarget() const;
	void SetDropTargetHighlight(NodeRef item);

protected:
	VDZLRESULT On_WM_NOTIFY(VDZWPARAM wParam, VDZLPARAM lParam);
	VDZLRESULT FixLabelEditWndProcA(VDZHWND hwnd, VDZUINT msg, VDZWPARAM wParam, VDZLPARAM lParam);
	VDZLRESULT FixLabelEditWndProcW(VDZHWND hwnd, VDZUINT msg, VDZWPARAM wParam, VDZLPARAM lParam);

	int			mNextTextIndex;
	VDStringW mTextW[3];
	VDStringA mTextA[3];
	VDZHFONT	mhfontBold;
	bool		mbCreatedBoldFont;

	void (*mPrevEditWndProc)();
	VDFunctionThunk *mpEditWndProcThunk;

	VDEvent<VDUIProxyTreeViewControl, int> mEventItemSelectionChanged;
	VDEvent<VDUIProxyTreeViewControl, bool *> mEventItemDoubleClicked;
	VDEvent<VDUIProxyTreeViewControl, BeginEditEvent *> mEventItemBeginEdit;
	VDEvent<VDUIProxyTreeViewControl, EndEditEvent *> mEventItemEndEdit;
	VDEvent<VDUIProxyTreeViewControl, GetDispAttrEvent *> mEventItemGetDisplayAttributes;
	vdfunction<void(const BeginDragEvent& event)> mpOnBeginDrag;
};

/////////////////////////////////////////////////////////////////////////////

class VDUIProxyEditControl final : public VDUIProxyControl {
public:
	VDUIProxyEditControl();
	~VDUIProxyEditControl();

	VDStringW GetText() const;
	void SetText(const wchar_t *s);

	void SetOnTextChanged(vdfunction<void(VDUIProxyEditControl *)> fn);

private:
	VDZLRESULT On_WM_COMMAND(VDZWPARAM wParam, VDZLPARAM lParam);

	vdfunction<void(VDUIProxyEditControl *)> mpOnTextChanged;
};

/////////////////////////////////////////////////////////////////////////////

class VDUIProxyRichEditControl final : public VDUIProxyControl {
public:
	VDUIProxyRichEditControl();
	~VDUIProxyRichEditControl();

	static void AppendEscapedRTF(VDStringA& buf, const wchar_t *str);

	void EnsureCaretVisible();

	void SetCaretPos(int lineIndex, int charIndex);

	void SetText(const wchar_t *s);
	void SetTextRTF(const char *s);

	void SetReadOnlyBackground();

	void SetOnTextChanged(vdfunction<void()> fn);

private:
	VDZLRESULT On_WM_COMMAND(VDZWPARAM wParam, VDZLPARAM lParam);

	vdfunction<void()> mpOnTextChanged;
};

/////////////////////////////////////////////////////////////////////////////

class VDUIProxyButtonControl final : public VDUIProxyControl {
public:
	VDUIProxyButtonControl();
	~VDUIProxyButtonControl();

	bool GetChecked() const;
	void SetChecked(bool enable);

	void SetOnClicked(vdfunction<void()> fn);

private:
	VDZLRESULT On_WM_COMMAND(VDZWPARAM wParam, VDZLPARAM lParam);

	vdfunction<void()> mpOnClicked;
};

#endif
