//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
// This class is a message box that has two buttons, ok and cancel instead of
// just the ok button of a message box. We use a message box class for the ok button
// and implement another button here.
//=============================================================================//

#include <math.h>
#define PROTECTED_THINGS_DISABLE

#include <vgui/IInput.h>
#include <vgui/ISystem.h>
#include <vgui/ISurface.h>
#include <vgui/IScheme.h>
#include <vgui/IVGui.h>
#include <vgui/IPanel.h>

#include <vgui_controls/Tooltip.h>
#include <vgui_controls/TextEntry.h>
#include <vgui_controls/Controls.h>

// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>

using namespace vgui;


//------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------
static vgui::DHANDLE< TextEntry > s_TooltipWindow;
static int s_iTooltipWindowCount = 0;


//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
BaseTooltip::BaseTooltip(Panel *parent, const char *text) 
{
	SetText(text);

	_displayOnOneLine = false;
	_makeVisible = false;
	_isDirty = false;
	_enabled = true;

	_tooltipDelay = 500; // default delay for opening tooltips
	_delay = 0;
}

//-----------------------------------------------------------------------------
// Purpose: Reset the tooltip delay timer
//-----------------------------------------------------------------------------
void BaseTooltip::ResetDelay()
{
	_isDirty = true;
	_delay = system()->GetTimeMillis() + _tooltipDelay;
}

//-----------------------------------------------------------------------------
// Purpose: Set the tooltip delay before a tooltip comes up.
//-----------------------------------------------------------------------------
void BaseTooltip::SetTooltipDelay( int tooltipDelay )
{
	_tooltipDelay = tooltipDelay;
}

//-----------------------------------------------------------------------------
// Purpose: Get the tooltip delay before a tooltip comes up.
//-----------------------------------------------------------------------------
int BaseTooltip::GetTooltipDelay()
{
	return _tooltipDelay;
}

//-----------------------------------------------------------------------------
// Purpose: Set the tool tip to display on one line only
//			Default is multiple lines.
//-----------------------------------------------------------------------------
void BaseTooltip::SetTooltipFormatToSingleLine()
{
	_displayOnOneLine = true;
	_isDirty = true;
}

//-----------------------------------------------------------------------------
// Purpose: Set the tool tip to display on multiple lines.
//-----------------------------------------------------------------------------
void BaseTooltip::SetTooltipFormatToMultiLine()
{
	_displayOnOneLine = false;
	_isDirty = true;
}

//-----------------------------------------------------------------------------
// Purpose: Display the tooltip
//-----------------------------------------------------------------------------
void BaseTooltip::ShowTooltip(Panel *currentPanel)
{
	_makeVisible = true;

	PerformLayout();
}

void BaseTooltip::SetEnabled( bool bState )
{
	_enabled = bState;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool BaseTooltip::ShouldLayout( void )
{
	if (!_makeVisible)
		return false;

	if (_delay > system()->GetTimeMillis())
		return false;	

	// We only need to layout when we first become visible
	if ( !_isDirty )
		return false;

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Display the tooltip
//-----------------------------------------------------------------------------
void BaseTooltip::HideTooltip()
{
	_makeVisible = false;
	_isDirty = true;
}

//-----------------------------------------------------------------------------
// Purpose: Set the tooltip text
//-----------------------------------------------------------------------------
void BaseTooltip::SetText(const char *text)
{
	_isDirty = true;

	if (!text)
	{
		text = "";
	}

	if (m_Text.Size() > 0)
	{
		m_Text.RemoveAll();
	}

	for (unsigned int i = 0; i < strlen(text); i++)
	{
		m_Text.AddToTail(text[i]);
	}
	m_Text.AddToTail('\0');
	
	if (s_TooltipWindow.Get() && m_pParent == s_TooltipWindow.Get()->GetParent())
	{
		s_TooltipWindow->SetText(m_Text.Base());
	}
}

//-----------------------------------------------------------------------------
// Purpose: Get the tooltip text
//-----------------------------------------------------------------------------
const char *BaseTooltip::GetText()
{
	return m_Text.Base();
}

//-----------------------------------------------------------------------------
// Purpose: Position the tool tip
//-----------------------------------------------------------------------------
void BaseTooltip::PositionWindow( Panel *pTipPanel )
{
	int iTipW, iTipH;
	pTipPanel->GetSize( iTipW, iTipH );

	int cursorX, cursorY;
	input()->GetCursorPos(cursorX, cursorY);

	int wide, tall;
	surface()->GetScreenSize(wide, tall);

	int iParentX = 0, iParentY = 0;
	if ( !pTipPanel->IsPopup() )
	{
		pTipPanel->GetParent()->GetPos( iParentX, iParentY );
		pTipPanel->GetParent()->LocalToScreen( iParentX, iParentY );
	}

	cursorX -= iParentX;
	cursorY -= iParentY;

	if (wide - iTipW > cursorX)
	{
		cursorY += 20;
		// menu hanging right
		if (tall - iTipH > cursorY)
		{
			// menu hanging down
			pTipPanel->SetPos(cursorX, cursorY);
		}
		else
		{
			// menu hanging up
			pTipPanel->SetPos(cursorX, cursorY - iTipH - 20);
		}
	}
	else
	{
		// menu hanging left
		if (tall - iTipH > cursorY)
		{
			// menu hanging down
			pTipPanel->SetPos(cursorX - iTipW, cursorY);
		}
		else
		{
			// menu hanging up
			pTipPanel->SetPos(cursorX - iTipW, cursorY - iTipH - 20 );
		}
	}	
}


//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
TextTooltip::TextTooltip(Panel *parent, const char *text) : BaseTooltip( parent, text )
{
	if (!s_TooltipWindow.Get())
	{
		s_TooltipWindow = new TextEntry(NULL, "tooltip");

 		s_TooltipWindow->InvalidateLayout(false, true);

		// this bit of hackery is necessary because tooltips don't get ApplySchemeSettings called from their parents
		IScheme *pScheme = scheme()->GetIScheme( s_TooltipWindow->GetScheme() );
		s_TooltipWindow->SetBgColor(s_TooltipWindow->GetSchemeColor("Tooltip.BgColor", s_TooltipWindow->GetBgColor(), pScheme));
		s_TooltipWindow->SetFgColor(s_TooltipWindow->GetSchemeColor("Tooltip.TextColor", s_TooltipWindow->GetFgColor(), pScheme));
		s_TooltipWindow->SetBorder(pScheme->GetBorder("ToolTipBorder"));
		s_TooltipWindow->SetFont( pScheme->GetFont("DefaultSmall", s_TooltipWindow->IsProportional()));
	}
	s_iTooltipWindowCount++;

	// this line prevents the main window from losing focus
	// when a tooltip pops up
	s_TooltipWindow->MakePopup(false, true);
	s_TooltipWindow->SetKeyBoardInputEnabled( false );
	s_TooltipWindow->SetMouseInputEnabled( false );
	
	SetText(text);
	s_TooltipWindow->SetText(m_Text.Base());
	s_TooltipWindow->SetEditable(false);
	s_TooltipWindow->SetMultiline(true);
	s_TooltipWindow->SetVisible(false);
}


//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
TextTooltip::~TextTooltip()
{
	if (--s_iTooltipWindowCount < 1)
	{
		if ( s_TooltipWindow.Get() )
		{
			s_TooltipWindow->MarkForDeletion();
		}
		s_TooltipWindow = NULL;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Set the tooltip text
//-----------------------------------------------------------------------------
void TextTooltip::SetText(const char *text)
{
	BaseTooltip::SetText( text );
	
	if (s_TooltipWindow.Get())
	{
		s_TooltipWindow->SetText(m_Text.Base());
	}
}

//-----------------------------------------------------------------------------
// Purpose: gets the font from the scheme
//-----------------------------------------------------------------------------
void TextTooltip::ApplySchemeSettings(IScheme *pScheme)
{
	if ( s_TooltipWindow )
	{
		s_TooltipWindow->SetFont(pScheme->GetFont("DefaultSmall", s_TooltipWindow->IsProportional()));
	}
}

//-----------------------------------------------------------------------------
// Purpose: Display the tooltip
//-----------------------------------------------------------------------------
void TextTooltip::ShowTooltip(Panel *currentPanel)
{
	if ( s_TooltipWindow.Get() )
	{
		int nLen = s_TooltipWindow->GetTextLength();

		if ( nLen <= 0 )
		{
			// Empty tool tip, no need to show it
			_makeVisible = false;
			return;
		}

		char *pBuf = (char*)_alloca( nLen+1 );
		s_TooltipWindow->GetText( pBuf, nLen+1 );
		Panel *pCurrentParent = s_TooltipWindow->GetParent();

		_isDirty = _isDirty || ( pCurrentParent != currentPanel );
		s_TooltipWindow->SetText( m_Text.Base() );
		s_TooltipWindow->SetParent(currentPanel);
	}
	BaseTooltip::ShowTooltip( currentPanel );
}

//-----------------------------------------------------------------------------
// Purpose: Display the tooltip
//-----------------------------------------------------------------------------
void TextTooltip::PerformLayout()
{
	if ( !ShouldLayout() )
		return;
	// we're ready, just make us visible
	if ( !s_TooltipWindow.Get() )
		return;

	_isDirty = false;

	s_TooltipWindow->SetVisible(true);
	s_TooltipWindow->MakePopup( false, true );
	s_TooltipWindow->SetKeyBoardInputEnabled( false );
	s_TooltipWindow->SetMouseInputEnabled( false );

	// relayout the textwindow immediately so that we know it's size
	//m_pTextEntry->InvalidateLayout(true);

	SizeTextWindow();
	PositionWindow( s_TooltipWindow );
}

//-----------------------------------------------------------------------------
// Purpose: Size the text window so all the text fits inside it.
//-----------------------------------------------------------------------------
void TextTooltip::SizeTextWindow()
{
	if ( !s_TooltipWindow.Get() )
		return;

	if (_displayOnOneLine)
	{
		// We want the tool tip to be one line
		s_TooltipWindow->SetMultiline(false);
		s_TooltipWindow->SetToFullWidth();
	}
	else
	{
		// We want the tool tip to be one line
		s_TooltipWindow->SetMultiline(false);
		s_TooltipWindow->SetToFullWidth();
		int wide, tall;
		s_TooltipWindow->GetSize( wide, tall );
		double newWide = sqrt( (2.0/1) * wide * tall );
		double newTall = (1/2) * newWide;
		s_TooltipWindow->SetMultiline(true);
		s_TooltipWindow->SetSize((int)newWide, (int)newTall );
		s_TooltipWindow->SetToFullHeight();
		
		s_TooltipWindow->GetSize( wide, tall );
		
		if (( wide < 100 ) && ( s_TooltipWindow->GetNumLines() == 2) ) 
		{
			s_TooltipWindow->SetMultiline(false);
			s_TooltipWindow->SetToFullWidth();	
		}
		else
		{
			
			while ( (float)wide/(float)tall < 2 )
			{
				s_TooltipWindow->SetSize( wide + 1, tall );
				s_TooltipWindow->SetToFullHeight();
				s_TooltipWindow->GetSize( wide, tall );
			}
		}
		s_TooltipWindow->GetSize( wide, tall );
	//	ivgui()->DPrintf("End Ratio: %f\n", (float)wide/(float)tall);		
	}
}

//-----------------------------------------------------------------------------
// Purpose: Display the tooltip
//-----------------------------------------------------------------------------
void TextTooltip::HideTooltip()
{
	if ( s_TooltipWindow.Get() )
	{
		s_TooltipWindow->SetVisible(false);
	}

	BaseTooltip::HideTooltip();
}