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


#include "Gwen/Gwen.h"
#include "Gwen/Skin.h"
#include "Gwen/Controls/DockBase.h"
#include "Gwen/Controls/DockedTabControl.h"
#include "Gwen/Controls/Highlight.h"
#include "Gwen/DragAndDrop.h"
#include "Gwen/Controls/Resizer.h"

using namespace Gwen;
using namespace Gwen::Controls;


GWEN_CONTROL_CONSTRUCTOR( DockBase )
{
	SetPadding( Padding( 1, 1, 1, 1 ) );
	SetSize( 200, 200 );

	m_DockedTabControl = NULL;
	m_Left = NULL;
	m_Right = NULL;
	m_Top = NULL;
	m_Bottom = NULL;

	m_bDrawHover = false;
}

TabControl* DockBase::GetTabControl()
{ 
	return m_DockedTabControl; 
}

void DockBase::SetupChildDock( int iPos )
{
	if ( !m_DockedTabControl )
	{
		m_DockedTabControl = new DockedTabControl( this );
		m_DockedTabControl->onLoseTab.Add( this, &DockBase::OnTabRemoved );
		m_DockedTabControl->SetTabStripPosition( Pos::Bottom );
		m_DockedTabControl->SetShowTitlebar( true );
	}

	Dock( iPos );

	int iSizeDirection = Pos::Left;
	if ( iPos == Pos::Left ) iSizeDirection = Pos::Right;
	if ( iPos == Pos::Top ) iSizeDirection = Pos::Bottom;
	if ( iPos == Pos::Bottom ) iSizeDirection = Pos::Top;

	ControlsInternal::Resizer* sizer = new ControlsInternal::Resizer( this );
		sizer->Dock( iSizeDirection );
		sizer->SetResizeDir( iSizeDirection );
		sizer->SetSize( 2, 2 );
		sizer->SetTarget( this );
}

void DockBase::Render( Skin::Base* /*skin*/ )
{
	//Gwen::Render->SetDrawColor( Colors::Black );
	//Gwen::Render->DrawLinedRect( GetRenderBounds() );
}

DockBase** DockBase::GetChildDockPtr( int iPos )
{
	if ( iPos == Pos::Left ) return &m_Left;
	if ( iPos == Pos::Right ) return &m_Right;
	if ( iPos == Pos::Top ) return &m_Top;
	if ( iPos == Pos::Bottom ) return &m_Bottom;

	return NULL;
}

DockBase* DockBase::GetChildDock( int iPos )
{
	DockBase** pDock = GetChildDockPtr( iPos );
	
	if ( !(*pDock) )
	{
		(*pDock) = new DockBase( this );
		(*pDock)->SetupChildDock( iPos );
	}
	else
	{
		(*pDock)->SetHidden( false );
	}

	return *pDock;
}

int DockBase::GetDroppedTabDirection( int x, int y )
{
	int w = Width();
	int h = Height();

	float top = (float)y / (float) h;
	float left = (float)x / (float) w;
	float right = (float)(w - x) /(float) w;
	float bottom = (float)(h - y) / (float) h;

	float minimum = GwenUtil_Min( GwenUtil_Min( GwenUtil_Min( top, left ), right ), bottom );
	m_bDropFar = ( minimum < 0.2f );
	if ( minimum > 0.3 ) return Pos::Fill;

	if ( top == minimum && (!m_Top || m_Top->Hidden()) ) return Pos::Top;
	if ( left == minimum && (!m_Left || m_Left->Hidden()) ) return Pos::Left;
	if ( right == minimum && (!m_Right || m_Right->Hidden()) ) return Pos::Right;
	if ( bottom == minimum && (!m_Bottom || m_Bottom->Hidden()) ) return Pos::Bottom;

	return Pos::Fill;
}

bool DockBase::DragAndDrop_CanAcceptPackage( Gwen::DragAndDrop::Package* pPackage )
{
	// A TAB button dropped 
	if ( pPackage->name == "TabButtonMove" )
		return true;

	// a TAB window dropped
	if ( pPackage->name == "TabWindowMove" )
		return true;

	return false;
}

bool DockBase::DragAndDrop_HandleDrop( Gwen::DragAndDrop::Package* pPackage, int x, int y )
{
	Gwen::Point pPos = CanvasPosToLocal( Gwen::Point( x, y ) );
	int dir = GetDroppedTabDirection( pPos.x, pPos.y );

	DockedTabControl* pAddTo = m_DockedTabControl;
	if ( dir == Pos::Fill && pAddTo == NULL ) return false;

	if ( dir != Pos::Fill )
	{
		DockBase* pDock = GetChildDock( dir );
		pAddTo = pDock->m_DockedTabControl;

		if ( !m_bDropFar ) pDock->BringToFront();
		else pDock->SendToBack();
	}

	if ( pPackage->name == "TabButtonMove" )
	{
		TabButton* pTabButton = gwen_cast<TabButton>( DragAndDrop::SourceControl );
		if ( !pTabButton ) return false;

		pAddTo->AddPage( pTabButton );
	}

	if ( pPackage->name == "TabWindowMove" )
	{
		DockedTabControl* pTabControl = gwen_cast<DockedTabControl>( DragAndDrop::SourceControl );
		if ( !pTabControl ) return false;
		if ( pTabControl == pAddTo ) return false;

		pTabControl->MoveTabsTo( pAddTo );
	}

	Invalidate();

	return true;
}

bool DockBase::IsEmpty()
{
	if ( m_DockedTabControl && m_DockedTabControl->TabCount() > 0 ) return false;

	if ( m_Left && !m_Left->IsEmpty() ) return false;
	if ( m_Right && !m_Right->IsEmpty() ) return false;
	if ( m_Top && !m_Top->IsEmpty() ) return false;
	if ( m_Bottom && !m_Bottom->IsEmpty() ) return false;

	return true;
}

void DockBase::OnTabRemoved( Gwen::Controls::Base* /*pControl*/ )
{
	DoRedundancyCheck();
	DoConsolidateCheck();
}

void DockBase::DoRedundancyCheck()
{
	if ( !IsEmpty() ) return;

	DockBase* pDockParent = gwen_cast<DockBase>( GetParent() );
	if ( !pDockParent ) return;

	pDockParent->OnRedundantChildDock( this );
}

void DockBase::DoConsolidateCheck()
{
	if ( IsEmpty() ) return;
	if ( !m_DockedTabControl ) return;
	if ( m_DockedTabControl->TabCount() > 0 ) return;

	if ( m_Bottom && !m_Bottom->IsEmpty() )
	{
		m_Bottom->m_DockedTabControl->MoveTabsTo( m_DockedTabControl );
		return;
	}

	if ( m_Top && !m_Top->IsEmpty() )
	{
		m_Top->m_DockedTabControl->MoveTabsTo( m_DockedTabControl );
		return;
	}

	if ( m_Left && !m_Left->IsEmpty() )
	{
		m_Left->m_DockedTabControl->MoveTabsTo( m_DockedTabControl );
		return;
	}

	if ( m_Right && !m_Right->IsEmpty() )
	{
		m_Right->m_DockedTabControl->MoveTabsTo( m_DockedTabControl );
		return;
	}
}

void DockBase::OnRedundantChildDock( DockBase* pDockBase )
{
	pDockBase->SetHidden( true );
	DoRedundancyCheck();
	DoConsolidateCheck();
}

void DockBase::DragAndDrop_HoverEnter( Gwen::DragAndDrop::Package* /*pPackage*/, int /*x*/, int /*y*/ )
{
	m_bDrawHover = true;
}

void DockBase::DragAndDrop_HoverLeave( Gwen::DragAndDrop::Package* /*pPackage*/ )
{
	m_bDrawHover = false;
}

void DockBase::DragAndDrop_Hover( Gwen::DragAndDrop::Package* /*pPackage*/, int x, int y )
{
	Gwen::Point pPos = CanvasPosToLocal( Gwen::Point( x, y ) );
	int dir = GetDroppedTabDirection( pPos.x, pPos.y );

	if ( dir == Pos::Fill )
	{
		if ( !m_DockedTabControl )
		{
			m_HoverRect = Gwen::Rect( 0, 0, 0, 0 );
			return;
		}

		m_HoverRect = GetInnerBounds();
		return;
	}

	m_HoverRect = GetRenderBounds();

	int HelpBarWidth = 0;

	if ( dir == Pos::Left )
	{
		HelpBarWidth = m_HoverRect.w * 0.25f;
		m_HoverRect.w = HelpBarWidth;
	}

	if ( dir == Pos::Right )
	{
		HelpBarWidth = m_HoverRect.w * 0.25f;
		m_HoverRect.x = m_HoverRect.w - HelpBarWidth;
		m_HoverRect.w = HelpBarWidth;
	}

	if ( dir == Pos::Top )
	{
		HelpBarWidth = m_HoverRect.h * 0.25f;
		m_HoverRect.h = HelpBarWidth;
	}

	if ( dir == Pos::Bottom )
	{
		HelpBarWidth = m_HoverRect.h * 0.25f;
		m_HoverRect.y = m_HoverRect.h - HelpBarWidth;
		m_HoverRect.h = HelpBarWidth;
	}

	if ( (dir == Pos::Top || dir == Pos::Bottom ) && !m_bDropFar )
	{
		if ( m_Left && m_Left->Visible() )
		{
			m_HoverRect.x += m_Left->Width();
			m_HoverRect.w -= m_Left->Width();
		}

		if ( m_Right && m_Right->Visible() )
		{
			m_HoverRect.w -= m_Right->Width();
		}
	}

	if ( (dir == Pos::Left || dir == Pos::Right ) && !m_bDropFar )
	{
		if ( m_Top && m_Top->Visible() )
		{
			m_HoverRect.y += m_Top->Height();
			m_HoverRect.h -= m_Top->Height();
		}

		if ( m_Bottom && m_Bottom->Visible() )
		{
			m_HoverRect.h -= m_Bottom->Height();
		}
	}
}

void DockBase::RenderOver( Skin::Base* skin )
{
	if ( !m_bDrawHover ) return;

	Gwen::Renderer::Base* render = skin->GetRender();

	render->SetDrawColor( Gwen::Color( 255, 100, 255, 20 ) );
	render->DrawFilledRect( GetRenderBounds() );

	if ( m_HoverRect.w == 0 ) return;

	render->SetDrawColor( Gwen::Color( 255, 100, 255, 100 ) );
	render->DrawFilledRect( m_HoverRect );

	render->SetDrawColor( Gwen::Color( 255, 100, 255, 200 ) );
	render->DrawLinedRect( m_HoverRect );
}