/*
DISCLAIMER

Auther: Mizan Rahman
Original publication location: http://www.codeproject.com/KB/dialog/WndResizer.aspx

This work is provided under the terms and condition described in The Code Project Open License (CPOL)
(http://www.codeproject.com/info/cpol10.aspx)

This disclaimer should not be removed and should exist in any reproduction of this work.
*/

#pragma once
#include "uxtheme.h"


#define ANCHOR_LEFT                   1
#define ANCHOR_TOP                    2
#define ANCHOR_RIGHT                  4
#define ANCHOR_BOTTOM                 8
#define ANCHOR_HORIZONTALLY_CENTERED  16
#define ANCHOR_VERTICALLY_CENTERED    32
#define ANCHOR_PRIORITY_RIGHT         64   // by defualt, left has higher priority
#define ANCHOR_PRIORITY_BOTTOM        128  // by defualt, top has higher priority
#define ANCHOR_VERTICALLY             (ANCHOR_TOP | ANCHOR_BOTTOM)
#define ANCHOR_HORIZONTALLY           (ANCHOR_LEFT | ANCHOR_RIGHT)
#define ANCHOR_ALL                    (ANCHOR_VERTICALLY | ANCHOR_HORIZONTALLY)

#define DOCK_NONE   0
#define DOCK_LEFT   1
#define DOCK_TOP    2
#define DOCK_RIGHT  3
#define DOCK_BOTTOM 4
#define DOCK_FILL   5


class CWndResizer 
{
public:
	CWndResizer(void);
	~CWndResizer(void);
private:

	class CPanel;
	typedef CList<CPanel *, CPanel *> CPanelList;

	typedef enum tagSplitterOrientation 
	{
		// a horizontal splitter container panel
		SPLIT_CONTAINER_H = 1,
		// a vertical splitter contianer panel
		SPLIT_CONTAINER_V = 2, 

	} SplitterOrientation;


	typedef enum tagFlowDirection 
	{
		// left to right flow direction
		LEFT_TO_RIGHT = 3,
		// top to bottom flow direction
		TOP_TO_BOTTOM = 4, 

	} FlowDirection;

	class CPanel : public CRect
	{
	public:
		CPanel();
		CPanel(const CRect * prc);
		virtual ~CPanel();
		UINT Anchor;
		UINT Dock;
		CString Name;
		CPanel * Parent;
		CPanelList Children;
		int LeftOffset;
		int TopOffset;
		int RightOffset;
		int BottomOffset; 
		CSize MinSize;
		CSize MaxSize;
		virtual void OnResized();
		virtual BOOL AddChild(CPanel * prc);
		virtual BOOL RemoveChild(CPanel * prc);
		virtual BOOL SetAnchor(UINT anchor);
		virtual BOOL SetMinSize(CSize & size);
		virtual BOOL SetMaxSize(CSize & size);
		virtual CString ToString();
		virtual CString GetTypeName();
		virtual CWnd * GetHookedWnd();
	private:
		void Init();
	};

	class CRootPanel : public CPanel
	{
	public:
		CRootPanel();
		~CRootPanel();
		virtual CWnd * GetHookedWnd();
		CWnd * m_pHookWnd;
		virtual CString GetTypeName();
	}; 

	class CUIPanel : public CPanel
	{
	public:
		CUIPanel(const CRect * prc, UINT uID);
		~CUIPanel();
		UINT m_uID;   // could be a resource ID or a HWND
		BOOL m_bOle;  // TRUE= m_uID is an ID of an ActiveX or OLE control, FALSE=regular windows control
		virtual CString GetTypeName();
	};


	class CVisualPanel : public CPanel
	{
	public:
		CVisualPanel();
		CVisualPanel(const CRect * prc);
		~CVisualPanel();
		virtual void Draw(CDC * pDC);
		BOOL m_bVisible;
		virtual CString GetTypeName();
		virtual void OnResized();
	private:
		CRect m_rcPrev;
	};

	class CGripperPanel : public CVisualPanel
	{
	public:
		CGripperPanel();
		CGripperPanel(const CRect * prc);
		~CGripperPanel();
		virtual void Draw(CDC * pDC);
		virtual CString GetTypeName();
	private:
		HTHEME m_hTheme;
		int m_iPartId;
		int m_iStateId;
		CString m_sClassName;
	};

	class CSplitterGripperPanel : public CVisualPanel
	{
	public:
		CSplitterGripperPanel(CWndResizer::SplitterOrientation type);
		~CSplitterGripperPanel();
		virtual void Draw(CDC * pDC);
		virtual CString GetTypeName();
	private:
		CWndResizer::SplitterOrientation m_OrienType;
	};

	class CSplitPanel : public CPanel
	{
	public:
		CSplitPanel(CPanel * pPanel);
		~CSplitPanel();
		virtual BOOL SetAnchor(UINT anchor);
		virtual BOOL AddChild(CPanel * prc);
		virtual BOOL RemoveChild(CPanel * prc);
		virtual CString GetTypeName();
	private:
		CPanel * m_pOriginalPanel;
	};


	class CSpitterPanel : public CPanel
	{
	public:
		CSpitterPanel(CWndResizer::SplitterOrientation type);
		CSpitterPanel(const CRect * prc, CWndResizer::SplitterOrientation type);
		~CSpitterPanel();
		virtual CString GetTypeName();
		CSplitterGripperPanel * m_pGrippePanel;
	private:
		CWndResizer::SplitterOrientation m_OrienType;
	};


	class CSplitContainer : public CPanel
	{
	public:
		CSplitContainer(CSplitPanel * pPanelA, CSplitPanel * pPanelB, CWndResizer::SplitterOrientation type);
		~CSplitContainer();
		virtual void OnResized();
		virtual BOOL AddChild(CPanel * prc);
		virtual BOOL RemoveChild(CPanel * prc);
		virtual CString GetTypeName();
		void SetSplitterPosition(int leftOfSpliter /* or topOfSpliter if vertical */);
		int GetSplitterPosition();
		void SetFixedPanel(short nFixedPanel /* 1=left or top; 2=right or bototm; other=no fixed panel */);
		short GetFixedPanel();
		void SetIsSplitterFixed(BOOL bFixed);
		BOOL GetIsSplitterFixed();
		void SetShowSplitterGrip(BOOL bShow);
		BOOL GetShowSplitterGrip();

		static CWndResizer::CSplitContainer * Create(CPanel * pPanelA, CPanel * pPanelB);

		CWndResizer::SplitterOrientation m_Orientation;
	private:
		BOOL m_IsSplitterFixed;
		short m_FixedPanel; // 1=left or top panel; 2=right or bottom panel, otherwise no fixed panel

		double m_nRatio;
		int m_nSplitterSize; // for horizontal, it is the splitter width, otherwise it is the splitter height
		void GetSplitArea(CRect * pSplitterPanel);
		int GetSplitterSize(CPanel * pLeftPanel, CPanel * pRightPanel);
		void UpdateRatio();

		CSplitPanel * m_pPanelA;
		CSplitPanel * m_pPanelB;
		CSpitterPanel * m_pSplitter;
	};


	class CFlowLayoutPanel : public CPanel
	{
	public:
		CFlowLayoutPanel();
		CFlowLayoutPanel(const CRect * prc);
		~CFlowLayoutPanel();
		virtual void OnResized();
		virtual CString GetTypeName();
		void SetFlowDirection(CWndResizer::FlowDirection direction);
		CWndResizer::FlowDirection GetFlowDirection();
		void SetItemSpacingX(int nSpace);
		int GetItemSpacingX();
		void SetItemSpacingY(int nSpace);
		int GetItemSpacingY();

	private:
		int m_nItemSpacingX;
		int m_nItemSpacingY;
		CWndResizer::FlowDirection m_nFlowDirection;
	};

public:
	BOOL SetAnchor(LPCTSTR panelName, UINT anchor);
	BOOL SetAnchor(UINT panelID, UINT anchor);
	BOOL GetAnchor(LPCTSTR panelName, UINT & anchor);
	BOOL GetAnchor(UINT panelID, UINT & anchor);

	BOOL GetAutoHandlePaint();
	BOOL SetAutoHandlePaint(BOOL bHandle);


	BOOL SetDock(LPCTSTR panelName, UINT anchor);
	BOOL SetDock(UINT panelID, UINT anchor);
	BOOL GetDock(LPCTSTR panelName, UINT & anchor);
	BOOL GetDock(UINT panelID, UINT & anchor);

	BOOL SetFixedPanel(LPCTSTR splitContainerName, short panel);
	BOOL GetFixedPanel(LPCTSTR splitContainerName, short & panel);

	BOOL SetIsSplitterFixed(LPCTSTR splitContainerName, BOOL fixed);
	BOOL GetIsSplitterFixed(LPCTSTR splitContainerName, BOOL &fixed);

	BOOL SetFlowDirection(LPCTSTR flowPanelName, short direction);
	BOOL GetFlowDirection(LPCTSTR flowPanelName, short &direction);

	BOOL SetFlowItemSpacingX(LPCTSTR flowPanelName, int nSpacing);
	BOOL GetFlowItemSpacingX(LPCTSTR flowPanelName, int &nSpacing);

	BOOL SetFlowItemSpacingY(LPCTSTR flowPanelName, int nSpacing);
	BOOL GetFlowItemSpacingY(LPCTSTR flowPanelName, int &nSpacing);

	BOOL Draw(CPaintDC * pDC);

	BOOL SetShowSplitterGrip(LPCTSTR splitContainerName, BOOL bShow);
	BOOL GetShowSplitterGrip(LPCTSTR splitContainerName, BOOL & bShow);

	BOOL SetParent(LPCTSTR panelName, LPCTSTR parentName);
	BOOL SetParent(UINT panelID, LPCTSTR parentName);
	BOOL SetParent(LPCTSTR panelName, UINT parentID);
	BOOL SetParent(UINT panelID, UINT parentID);
	BOOL GetParent(LPCTSTR panelName, CString & parentName);
	BOOL GetParent(UINT panelID, CString & parentName);

	BOOL SetMinimumSize(LPCTSTR panelName, CSize & size);
	BOOL SetMinimumSize(UINT panelID, CSize & size);
	BOOL GetMinimumSize(LPCTSTR panelName, CSize & size) ;
	BOOL GetMinimumSize(UINT panelID, CSize & size) ;

	BOOL SetMaximumSize(LPCTSTR panelName, CSize & size);
	BOOL SetMaximumSize(UINT panelID, CSize & size);
	BOOL GetMaximumSize(LPCTSTR panelName, CSize & size) ;
	BOOL GetMaximumSize(UINT panelID, CSize & size) ;

	void SetShowResizeGrip(BOOL show = TRUE);
	BOOL GetShowResizeGrip();
	BOOL Hook(CWnd * pWnd);
	BOOL Hook(CWnd * pWnd, CSize & size);
	BOOL Unhook();

	BOOL CreateFlowLayoutPanel(LPCTSTR panelName, const CRect * prcPanel);
	BOOL CreateFlowLayoutPanel(LPCTSTR panelName, const CUIntArray * parrID, BOOL setAsChildren = FALSE);

	BOOL CreatePanel(LPCTSTR panelName, const CRect * prcPanel);
	BOOL CreatePanel(LPCTSTR panelName, const CUIntArray * parrID, BOOL setAsChildren = FALSE);

	BOOL CreatePanel(UINT uID);

	BOOL CreateSplitContainer(LPCTSTR splitContainerName, LPCTSTR panelNameA, LPCTSTR panelNameB);    
	BOOL CreateSplitContainer(LPCTSTR splitContainerName, LPCTSTR panelNameA, UINT panelIDB);  
	BOOL CreateSplitContainer(LPCTSTR splitContainerName, UINT panelIDA, UINT panelIDB);  
	BOOL CreateSplitContainer(LPCTSTR splitContainerName, UINT panelIDA, LPCTSTR panelNameB); 

	// useful for persisting UI layout
	BOOL SetSplitterPosition(LPCTSTR splitContainerName, UINT position);  
	BOOL GetSplitterPosition(LPCTSTR splitContainerName, UINT & position);  

	BOOL InvokeOnResized();

	CString GetDebugInfo();

private:

	CWnd * m_pHookedWnd;
	CRootPanel root;
	CPanel * m_pCaptured;
	HCURSOR m_hOldCursor;
	int m_splitterOffset;  

	BOOL m_bAutoHandlePaint;

	void OnPaint();
	void OnSize(UINT nType, int cx, int cy);
	void OnSizing(UINT fwSide, LPRECT pRect);
	void OnScroll();
	void OnDestroy();
	void OnLButtonDown(UINT nFlags, CPoint point);
	void OnMouseMove(UINT nFlags, CPoint point);
	void OnLButtonUp(UINT nFlags, CPoint point);

	CWndResizer::CPanel * FindPanelByName(CWndResizer::CPanel * pRoot, LPCTSTR name);
	CWndResizer::CPanel * FindSplitterFromPoint(CWndResizer::CPanel * pRoot, CPoint point);
	void GetUIPanels(CWndResizer::CPanel * pRoot, CPanelList * pList, BOOL bOle);
	void GetVisualPanels(CWndResizer::CPanel * pRoot, CPanelList * pList);

	void ResizeUI(CWndResizer::CPanel * pRoot);

	CString IdToName(UINT uID);
	CWndResizer::CUIPanel * CreateUIPanel(UINT uID);
	CWndResizer::CUIPanel * GetUIPanel(UINT uID);

	void UpdateSplitterOffset(CPoint ptCurr);
	void GetDebugInfo(CPanel * pRoot, CString & info, CString  indent);
	void GetTrueClientRect(CWnd * pWnd, CRect * prc);
	void EnsureRootMinMax();

	WNDPROC m_pfnWndProc;
	static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);


};

