/*
	GWEN
	Copyright (c) 2010 Facepunch Studios
	See license in Gwen.h
*/

#pragma once
#ifndef GWEN_CONTROLS_BASE_H
#define GWEN_CONTROLS_BASE_H

#include "Gwen/Exports.h"
#include "Gwen/Structures.h"
#include "Gwen/BaseRender.h"
#include "Gwen/Events.h"
#include <list>
#include <map>

#define GWEN_DECLARE_CAST(T)\
virtual class T* DynamicCast##T() { return 0;}\
virtual const class T* DynamicCast##T() const { return 0;}\

#define GWEN_IMPLEMENT_CAST(T)\
virtual class T* DynamicCast##T() { return (T*)this;}\
virtual const class T* DynamicCast##T() const { return (T*)this;}\


namespace Gwen 
{
	namespace ControlsInternal
	{
		class ColorDisplay;
		class Resizer;

	};

	namespace Pos
	{
		enum
		{
			None		= 0,
			Left		= (1 << 1),
			Right		= (1 << 2),
			Top			= (1 << 3),
			Bottom		= (1 << 4),
			CenterV		= (1 << 5),
			CenterH		= (1 << 6),
			Fill		= (1 << 7),
			Center		= CenterV | CenterH,
		};
	}

	namespace Skin
	{
		class Base;
	}

	namespace Controls
	{
		class Canvas;

		namespace Layout
		{
			class TableRow;
		};

		


		class GWEN_EXPORT Base : public Event::Handler
		{
			public:

				GWEN_DECLARE_CAST(TabButton)
				GWEN_DECLARE_CAST(DockedTabControl)
				virtual class Layout::TableRow* DynamicCastLayoutTableRow() { return 0;}
				virtual const class Layout::TableRow* DynamicCastLayoutTableRow() const { return 0;}

			

				GWEN_DECLARE_CAST(TextBoxNumeric)
				GWEN_DECLARE_CAST(HorizontalSlider)
				GWEN_DECLARE_CAST(DockBase)
				GWEN_DECLARE_CAST(MenuItem)
				GWEN_DECLARE_CAST(PropertyRow)
				GWEN_DECLARE_CAST(WindowControl)
				
				GWEN_DECLARE_CAST(TreeControl)
				GWEN_DECLARE_CAST(TreeNode)
				GWEN_DECLARE_CAST(HSVColorPicker)
				GWEN_DECLARE_CAST(TabControl)

				GWEN_DECLARE_CAST(TabControlInner)
				GWEN_DECLARE_CAST(GroupBox)
				GWEN_DECLARE_CAST(Properties)
				GWEN_DECLARE_CAST(RadioButton)
				GWEN_DECLARE_CAST(LabeledRadioButton)

			

				virtual class ::Gwen::ControlsInternal::Resizer* DynamicCastResizer() { return 0;}
				virtual const class ::Gwen::ControlsInternal::Resizer* DynamicCastResizer() const { return 0;}


				virtual class ::Gwen::ControlsInternal::ColorDisplay* DynamicCastColorDisplay() { return 0;}
				virtual const class ::Gwen::ControlsInternal::ColorDisplay* DynamicCastColorDisplay() const { return 0;}


				


				typedef std::list<Base*> List;

				typedef std::map<Gwen::UnicodeString, Gwen::Event::Caller*> AccelMap;

				Base( Base* pParent );
				virtual ~Base();

				virtual void DelayedDelete();

				virtual void SetParent( Controls::Base* pParent );
				virtual Controls::Base* GetParent() const { return m_Parent; }
				virtual Controls::Canvas* GetCanvas();
				
				virtual Base::List& GetChildren(){ if ( m_InnerPanel ) return m_InnerPanel->GetChildren(); return Children; }
				virtual bool IsChild( Controls::Base* pChild );
				virtual int NumChildren();
				virtual bool SizeToChildren( bool w = true, bool h = true );
				virtual Gwen::Point ChildrenSize();
				virtual Controls::Base* FindChildByName( const Gwen::String& name, bool bRecursive = false );

				virtual void SetName(Gwen::String name) { m_Name = name; }
				virtual const Gwen::String& GetName() { return m_Name; }

				virtual void Think(){}
				virtual void ExpandAll(){}
				virtual void SizeToContents(){}
				virtual bool IsActive() { return false;}
			

				virtual void AddChild( Controls::Base* pChild );

				virtual void RemoveChild( Controls::Base* pParent );
		protected:

				
				
				virtual void OnChildAdded( Controls::Base* pChild );
				virtual void OnChildRemoved( Controls::Base* pChild );

		public:

				virtual void RemoveAllChildren();

				virtual void SendToBack( void );
				virtual void BringToFront( void );
				virtual void BringNextToControl( Controls::Base* pChild, bool bBehind );

				virtual Gwen::Point LocalPosToCanvas( const Gwen::Point& in );
				virtual Gwen::Point CanvasPosToLocal( const Gwen::Point& in );

				virtual void Dock( int iDock );
				virtual int GetDock();

				virtual void RestrictToParent( bool restrict ) { m_bRestrictToParent = restrict; }
				virtual bool ShouldRestrictToParent() { return m_bRestrictToParent; }

				virtual int X() const 
				{ 
					return m_Bounds.x; 
				}
				virtual int Y() const 
				{ 
					return m_Bounds.y; 
				}
				virtual int Width() const 
				{ 
					return m_Bounds.w; 
				}
				virtual int Height() const 
				{ 
					return m_Bounds.h; 
				}
				virtual int Bottom() const 
				{ 
					return m_Bounds.y + m_Bounds.h + m_Margin.bottom; 
				}
				virtual int Right() const 
				{
					return m_Bounds.x + m_Bounds.w + m_Margin.right; 
				}

				virtual const Margin& GetMargin() const { return m_Margin; }
				virtual const Padding& GetPadding() const { return m_Padding; }

				virtual void SetPos( int x, int y );
				virtual void SetWidth( int w ) 
				{ 
					SetSize( w, Height()); 
				}
				virtual void SetHeight( int h ) 
				{ 
SetSize( Width(), h); 
				}
				virtual bool SetSize( int w, int h );
				virtual bool SetBounds( int x, int y, int w, int h );
				virtual bool SetBounds( const Gwen::Rect& bounds );

				virtual void SetPadding( const Padding& padding );
				virtual void SetMargin( const Margin& margin );

				// MoveTo is identical to SetPos except it uses ShouldRestrictToParent()
				virtual void MoveTo (int x, int y );
				virtual void MoveBy (int x, int y );

				virtual const Gwen::Rect& GetBounds() const 
				{ 
					return m_Bounds; 
				}

				virtual Controls::Base* GetControlAt( int x, int y );




			protected:

				virtual void OnBoundsChanged( Gwen::Rect oldBounds );
				virtual void OnChildBoundsChanged( Gwen::Rect oldChildBounds, Base* pChild );

				virtual void OnScaleChanged();

			public:

				// Innerbounds is the area inside the control that 
				// doesn't have child controls docked to it.
				virtual const Gwen::Rect& GetInnerBounds() const { return m_InnerBounds; }

			protected:

				Gwen::Rect m_InnerBounds;

			public:

				virtual const Gwen::Rect& GetRenderBounds() const{ return m_RenderBounds; }

			protected:

				virtual void UpdateRenderBounds();

			public:

				virtual void DoRender( Gwen::Skin::Base* skin );
				virtual void DoCacheRender( Gwen::Skin::Base* skin, Gwen::Controls::Base* pMaster );

			protected:

				virtual void Render( Gwen::Skin::Base* skin );
				virtual void RenderUnder( Gwen::Skin::Base* /*skin*/ ){};
				virtual void RenderOver( Gwen::Skin::Base* /*skin*/ ){};
				virtual void RenderFocus( Gwen::Skin::Base* /*skin*/ );

			public:	

				virtual void SetHidden( bool hidden ) 
				{ 
					if ( m_bHidden == hidden ) 
						return; 
					m_bHidden = hidden; 
					Invalidate(); 
				}
				virtual bool Hidden() const; // Returns true only if this control is hidden
				virtual bool Visible() const; // Returns false if this control or its parents are hidden
				virtual void Hide(){ SetHidden( true ); }
				virtual void Show(){ SetHidden( false ); }

				//Skin
				virtual void SetSkin( Skin::Base* skin, bool doChildren = false );
				virtual Gwen::Skin::Base* GetSkin( void );

				// Background drawing
				virtual bool ShouldDrawBackground(){ return m_bDrawBackground; }
				virtual void SetShouldDrawBackground( bool b ){ m_bDrawBackground =b; }

			protected:

				virtual void OnSkinChanged( Gwen::Skin::Base* newSkin );

			public:

				virtual void OnMouseMoved( int x, int y, int deltaX, int deltaY );
				virtual bool OnMouseWheeled( int iDelta );
				virtual void OnMouseClickLeft( int /*x*/, int /*y*/, bool /*bDown*/ ){};
				virtual void OnMouseClickRight( int /*x*/, int /*y*/, bool /*bDown*/ ){}
				virtual void OnMouseDoubleClickLeft( int x, int y ){ OnMouseClickLeft( x, y, true ); };
				virtual void OnMouseDoubleClickRight( int x, int y ){ OnMouseClickRight( x, y, true ); };
				virtual void OnLostKeyboardFocus(){}
				virtual void OnKeyboardFocus(){}

				virtual void SetMouseInputEnabled( bool b )	{ m_bMouseInputEnabled = b; }
				virtual bool GetMouseInputEnabled()	{ return m_bMouseInputEnabled; }
		
				virtual void SetKeyboardInputEnabled( bool b ){ m_bKeyboardInputEnabled = b; }
				virtual bool GetKeyboardInputEnabled() const { return m_bKeyboardInputEnabled; }
				virtual bool NeedsInputChars(){ return false; }

				virtual bool OnChar( Gwen::UnicodeChar /*c*/ ){ return false; }

				virtual bool OnKeyPress( int iKey, bool bPress = true );
				virtual bool OnKeyRelease( int iKey );

				virtual void OnPaste(Controls::Base* /*pFrom*/){}
				virtual void OnCopy(Controls::Base* /*pFrom*/){}
				virtual void OnCut(Controls::Base* /*pFrom*/){}
				virtual void OnSelectAll(Controls::Base* /*pFrom*/){}

				virtual bool OnKeyTab( bool bDown );
				virtual bool OnKeySpace( bool /*bDown*/ ){ return false; }
				virtual bool OnKeyReturn( bool /*bDown*/ ){ return false; }
				virtual bool OnKeyBackspace( bool /*bDown*/ ){ return false; }
				virtual bool OnKeyDelete( bool /*bDown*/ ){ return false; }
				virtual bool OnKeyRight( bool /*bDown*/ ){ return false; }
				virtual bool OnKeyLeft( bool /*bDown*/ ){ return false; }
				virtual bool OnKeyHome( bool /*bDown*/ ){ return false; }
				virtual bool OnKeyEnd( bool /*bDown*/ ){ return false; }
				virtual bool OnKeyUp( bool /*bDown*/ ){ return false; }
				virtual bool OnKeyDown( bool /*bDown*/ ){ return false; }
				virtual bool OnKeyEscape( bool /*bDown*/ ) { return false; }

				virtual void OnMouseEnter();
				virtual void OnMouseLeave();
				virtual bool IsHovered();
				virtual bool ShouldDrawHover();

				virtual void Touch();
				virtual void OnChildTouched( Controls::Base* pChild );

				virtual bool IsOnTop();

				virtual bool HasFocus();
				virtual void Focus();
				virtual void Blur();

				//Other
				virtual void SetDisabled( bool active ) { m_bDisabled = active; }
				virtual bool IsDisabled(){ return m_bDisabled; }
	
				virtual void Redraw(){ m_bCacheTextureDirty = true; if ( m_Parent ) m_Parent->Redraw(); }
				virtual void SetCacheToTexture() { m_bCacheToTexture = true; }
				virtual bool ShouldCacheToTexture() { return m_bCacheToTexture; }

				virtual void SetCursor( unsigned char c ){ m_Cursor = c; }
				virtual void UpdateCursor();

				virtual Gwen::Point GetMinimumSize(){ return Gwen::Point( 1, 1 ); }
				virtual Gwen::Point GetMaximumSize(){ return Gwen::Point( 4096, 4096 ); }

				virtual void SetToolTip( const String& strText );
				virtual void SetToolTip( const UnicodeString& strText );
				virtual void SetToolTip( Base* tooltip ) { m_ToolTip = tooltip; if ( m_ToolTip ){ m_ToolTip->SetParent( this ); m_ToolTip->SetHidden( true ); } }
				virtual Base* GetToolTip() { return m_ToolTip; }

				virtual bool IsMenuComponent();
				virtual void CloseMenus();

				virtual bool IsTabable() { return m_Tabable; }
				virtual void SetTabable( bool isTabable ) { m_Tabable = isTabable; }


				

				//Accelerator functionality 
				void DefaultAccel( Gwen::Controls::Base* /*pCtrl*/ ) { AcceleratePressed(); }
				virtual void AcceleratePressed() {};
				virtual bool AccelOnlyFocus() { return false; }
				virtual bool HandleAccelerator( Gwen::UnicodeString& accelerator );
				
				template <typename T>
				void AddAccelerator( Gwen::UnicodeString accelerator, T func, Gwen::Event::Handler* handler = NULL )
				{
					if ( handler == NULL )
						handler = this;
					Gwen::Event::Caller* caller = new Gwen::Event::Caller();
					caller->Add( handler, func );
					m_Accelerators[ accelerator ] = caller;
				}

				void AddAccelerator( Gwen::UnicodeString accelerator )
				{
					AddAccelerator( accelerator, &Base::DefaultAccel, this );
				}

				AccelMap m_Accelerators;

				// Default Events

				Gwen::Event::Caller	onHoverEnter;
				Gwen::Event::Caller	onHoverLeave;

				// Childrens List

				Base::List Children;

			protected:

				// The logical parent
				// It's usually what you expect, the control you've parented it to.
				Base* m_Parent;

				// If the innerpanel exists our children will automatically
				//  become children of that instead of us - allowing us to move
				//  them all around by moving that panel (useful for scrolling etc)
				Base* m_InnerPanel;

				// This is the panel's actual parent - most likely the logical 
				//  parent's InnerPanel (if it has one). You should rarely need this.
				Base* m_ActualParent;
				
				Base* m_ToolTip;
				
				Skin::Base* m_Skin;

				Gwen::Rect		m_Bounds;
				Gwen::Rect		m_RenderBounds;
				Padding		m_Padding;
				Margin		m_Margin;

				Gwen::String m_Name;

				
				bool m_bRestrictToParent;
				bool m_bDisabled;
				bool m_bHidden;
				bool m_bMouseInputEnabled;
				bool m_bKeyboardInputEnabled;
				bool m_bDrawBackground;

				int m_iDock;
			
				unsigned char	m_Cursor;

				bool m_Tabable;

			public:

				bool NeedsLayout(){ return m_bNeedsLayout; }
				void Invalidate();
				void InvalidateParent(){ if ( m_Parent ){ m_Parent->Invalidate(); } }
				void InvalidateChildren( bool bRecursive = false );
				void Position( int pos, int xpadding = 0, int ypadding = 0 );


			protected:

				virtual void RecurseLayout( Skin::Base* skin );
				virtual void Layout( Skin::Base* skin );
				virtual void PostLayout( Skin::Base* /*skin*/ ){};

				bool m_bNeedsLayout;
				bool m_bCacheTextureDirty;
				bool m_bCacheToTexture;

			//
			// Drag + Drop
			public:

				// Giver
				
				virtual void DragAndDrop_SetPackage( bool bDraggable, const String& strName = "", void* pUserData = NULL );
				virtual bool DragAndDrop_Draggable();
				virtual bool DragAndDrop_ShouldStartDrag(){ return true; }
				virtual void DragAndDrop_StartDragging( Gwen::DragAndDrop::Package* pPackage, int x, int y );
				virtual Gwen::DragAndDrop::Package* DragAndDrop_GetPackage( int x, int y );
				virtual void DragAndDrop_EndDragging( bool /*bSuccess*/, int /*x*/, int /*y*/ ){};
				
			protected:

				DragAndDrop::Package*	m_DragAndDrop_Package;

			public:

				// Receiver
				virtual void DragAndDrop_HoverEnter( Gwen::DragAndDrop::Package* /*pPackage*/, int /*x*/, int /*y*/ ){ }
				virtual void DragAndDrop_HoverLeave( Gwen::DragAndDrop::Package* /*pPackage*/ ){ }
				virtual void DragAndDrop_Hover( Gwen::DragAndDrop::Package* /*pPackage*/, int /*x*/, int /*y*/ ){};
				virtual bool DragAndDrop_HandleDrop( Gwen::DragAndDrop::Package* pPackage, int x, int y );
				virtual bool DragAndDrop_CanAcceptPackage( Gwen::DragAndDrop::Package* /*pPackage*/ ){ return false; }


			//
			// This is to be used by the client implementation
			// NOT HOOKS ETC.
			//
			public:

				void* GetUserData(){ return m_pUserData; }
				void SetUserData( void* pData ){ m_pUserData = pData; }

			private:

				void* m_pUserData;

			//
			// Useful anim shortcuts
			//
			public:
				#ifndef GWEN_NO_ANIMATION

				virtual void Anim_WidthIn( float fLength, float fDelay = 0.0f, float fEase = 1.0f );
				virtual void Anim_HeightIn( float fLength, float fDelay = 0.0f, float fEase = 1.0f );
				virtual void Anim_WidthOut( float fLength, bool bHide = true, float fDelay = 0.0f, float fEase = 1.0f );
				virtual void Anim_HeightOut( float fLength, bool bHide = true, float fDelay = 0.0f, float fEase = 1.0f );

				#endif
				
		};

	}
}

// To be placed in the controls .h definition.
#define GWEN_CONTROL( ThisName, BaseName )\
	public:\
	typedef BaseName BaseClass;\
	typedef ThisName ThisClass;\
	GWEN_IMPLEMENT_CAST(ThisName);\
	ThisName( Gwen::Controls::Base* pParent )

#define GWEN_CONTROL_INLINE( ThisName, BaseName )\
	GWEN_CONTROL( ThisName, BaseName ) : BaseClass( pParent )

#define GWEN_CONTROL_CONSTRUCTOR( ThisName )\
	ThisName::ThisName( Gwen::Controls::Base* pParent ) : BaseClass( pParent )

#endif
