//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

#include <assert.h>

#include <vgui/ISurface.h>
#include <vgui/IVGui.h>
#include <vgui/IPanel.h>
#include <vgui/VGUI.h>
#include <KeyValues.h>
#include <tier0/dbg.h>

#include <vgui_controls/Controls.h>
#include <vgui_controls/FocusNavGroup.h>
#include <vgui_controls/Panel.h>

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

using namespace vgui;

//-----------------------------------------------------------------------------
// Purpose: Constructor
// Input  : *panel - parent panel
//-----------------------------------------------------------------------------
FocusNavGroup::FocusNavGroup(Panel *panel) : _mainPanel(panel)
{
	_currentFocus = NULL;
	_topLevelFocus = false;
	_defaultButton = NULL;
    _currentDefaultButton = NULL;
}

//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
FocusNavGroup::~FocusNavGroup()
{
}

//-----------------------------------------------------------------------------
// Purpose: Sets the focus to the previous panel in the tab order
// Input  : *panel - panel currently with focus
//-----------------------------------------------------------------------------
bool FocusNavGroup::RequestFocusPrev(VPANEL panel)
{
	if(panel==NULL)
		return false;

	_currentFocus = NULL;
	int newPosition = 9999999;
	if (panel)
	{
		newPosition = ipanel()->GetTabPosition(panel);
	}

	bool bFound = false;
	bool bRepeat = true;
	Panel *best = NULL;
	while (1)
	{
		newPosition--;
		if (newPosition > 0)
		{
			int bestPosition = 0;

			// look for the next tab position
			for (int i = 0; i < _mainPanel->GetChildCount(); i++)
			{
				Panel *child = _mainPanel->GetChild(i);
				if (child && child->IsVisible() && child->IsEnabled() && child->GetTabPosition())
				{
					int tabPosition = child->GetTabPosition();
					if (tabPosition == newPosition)
					{
						// we've found the right tab
						best = child;
						bestPosition = newPosition;

						// don't loop anymore since we've found the correct panel
						break;
					}
					else if (tabPosition < newPosition && tabPosition > bestPosition)
					{
						// record the match since this is the closest so far
						bestPosition = tabPosition;
						best = child;
					}
				}
			}

			if (!bRepeat)
				break;

			if (best)
				break;
		}
		else
		{
			// reset new position for next loop
			newPosition = 9999999;
		}

		// haven't found an item

		if (!_topLevelFocus)
		{
			// check to see if we should push the focus request up
			if (_mainPanel->GetVParent() && _mainPanel->GetVParent() != surface()->GetEmbeddedPanel())
			{
				// we're not a top level panel, so forward up the request instead of looping
				if (ipanel()->RequestFocusPrev(_mainPanel->GetVParent(), _mainPanel->GetVPanel()))
				{
					bFound = true;
					SetCurrentDefaultButton(NULL);
					break;
				}
			}
		}

		// not found an item, loop back
		newPosition = 9999999;
		bRepeat = false;
	}

	if (best)
	{
		_currentFocus = best->GetVPanel();
		best->RequestFocus(-1);
		bFound = true;

        if (!CanButtonBeDefault(best->GetVPanel()))
        {
            if (_defaultButton)
            {
                SetCurrentDefaultButton(_defaultButton);
            }
			else
			{
				SetCurrentDefaultButton(NULL);

				// we need to ask the parent to set its default button
				if (_mainPanel->GetVParent())
				{
					ivgui()->PostMessage(_mainPanel->GetVParent(), new KeyValues("FindDefaultButton"), NULL);
				}
			}
        }
        else
        {
            SetCurrentDefaultButton(best->GetVPanel());
        }
	}
	return bFound;
}

//-----------------------------------------------------------------------------
// Purpose: Sets the focus to the previous panel in the tab order
// Input  : *panel - panel currently with focus
//-----------------------------------------------------------------------------
bool FocusNavGroup::RequestFocusNext(VPANEL panel)
{
	// basic recursion guard, in case user has set up a bad focus hierarchy
	static int stack_depth = 0;
	stack_depth++;

	_currentFocus = NULL;
	int newPosition = 0;
	if (panel)
	{
		newPosition = ipanel()->GetTabPosition(panel);
	}

	bool bFound = false;
	bool bRepeat = true;
	Panel *best = NULL;
	while (1)
	{
		newPosition++;
		int bestPosition = 999999;

		// look for the next tab position
		for (int i = 0; i < _mainPanel->GetChildCount(); i++)
		{
			Panel *child = _mainPanel->GetChild(i);
			if ( !child )
				continue;

			if (child && child->IsVisible() && child->IsEnabled() && child->GetTabPosition())
			{
				int tabPosition = child->GetTabPosition();
				if (tabPosition == newPosition)
				{
					// we've found the right tab
					best = child;
					bestPosition = newPosition;

					// don't loop anymore since we've found the correct panel
					break;
				}
				else if (tabPosition > newPosition && tabPosition < bestPosition)
				{
					// record the match since this is the closest so far
					bestPosition = tabPosition;
					best = child;
				}
			}
		}

		if (!bRepeat)
			break;

		if (best)
			break;

		// haven't found an item

		// check to see if we should push the focus request up
		if (!_topLevelFocus)
		{
			if (_mainPanel->GetVParent() && _mainPanel->GetVParent() != surface()->GetEmbeddedPanel())
			{
				// we're not a top level panel, so forward up the request instead of looping
				if (stack_depth < 15)
				{
					if (ipanel()->RequestFocusNext(_mainPanel->GetVParent(), _mainPanel->GetVPanel()))
					{
						bFound = true;
						SetCurrentDefaultButton(NULL);
						break;
					}

					// if we find one then we break, otherwise we loop
				}
			}
		}
		
		// loop back
		newPosition = 0;
		bRepeat = false;
	}

	if (best)
	{
		_currentFocus = best->GetVPanel();
		best->RequestFocus(1);
		bFound = true;

        if (!CanButtonBeDefault(best->GetVPanel()))
        {
            if (_defaultButton)
			{
                SetCurrentDefaultButton(_defaultButton);
			}
			else
			{
				SetCurrentDefaultButton(NULL);

				// we need to ask the parent to set its default button
				if (_mainPanel->GetVParent())
				{
					ivgui()->PostMessage(_mainPanel->GetVParent(), new KeyValues("FindDefaultButton"), NULL);
				}
			}
        }
        else
        {
            SetCurrentDefaultButton(best->GetVPanel());
        }
	}

	stack_depth--;
	return bFound;
}

//-----------------------------------------------------------------------------
// Purpose: sets the panel that owns this FocusNavGroup to be the root in the focus traversal heirarchy
//-----------------------------------------------------------------------------
void FocusNavGroup::SetFocusTopLevel(bool state)
{
	_topLevelFocus = state;
}

//-----------------------------------------------------------------------------
// Purpose: sets panel which receives input when ENTER is hit
//-----------------------------------------------------------------------------
void FocusNavGroup::SetDefaultButton(Panel *panel)
{
	if ((panel == NULL && _defaultButton.Get() == NULL) || panel->GetVPanel() == _defaultButton.Get())
		return;

    Assert(CanButtonBeDefault(panel->GetVPanel()));

	_defaultButton = panel->GetVPanel();
    SetCurrentDefaultButton(_defaultButton);
}

//-----------------------------------------------------------------------------
// Purpose: sets panel which receives input when ENTER is hit
//-----------------------------------------------------------------------------
void FocusNavGroup::SetCurrentDefaultButton(VPANEL panel, bool sendCurrentDefaultButtonMessage)
{
	if (panel == _currentDefaultButton.Get())
		return;

	if ( sendCurrentDefaultButtonMessage && _currentDefaultButton.Get() != NULL)
	{
		ivgui()->PostMessage(_currentDefaultButton, new KeyValues("SetAsCurrentDefaultButton", "state", 0), NULL);
	}

	_currentDefaultButton = panel;

	if ( sendCurrentDefaultButtonMessage && _currentDefaultButton.Get() != NULL)
	{
		ivgui()->PostMessage(_currentDefaultButton, new KeyValues("SetAsCurrentDefaultButton", "state", 1), NULL);
	}
}

//-----------------------------------------------------------------------------
// Purpose: sets panel which receives input when ENTER is hit
//-----------------------------------------------------------------------------
VPANEL FocusNavGroup::GetCurrentDefaultButton()
{
	return _currentDefaultButton;
}

//-----------------------------------------------------------------------------
// Purpose: sets panel which receives input when ENTER is hit
//-----------------------------------------------------------------------------
VPANEL FocusNavGroup::GetDefaultButton()
{
	return _defaultButton;
}

//-----------------------------------------------------------------------------
// Purpose: finds the panel which is activated by the specified key
// Input  : code - the keycode of the hotkey
// Output : Panel * - NULL if no panel found
//-----------------------------------------------------------------------------
Panel *FocusNavGroup::FindPanelByHotkey(wchar_t key)
{
	for (int i = 0; i < _mainPanel->GetChildCount(); i++)
	{
		Panel *child = _mainPanel->GetChild(i);
		if ( !child )
			continue;

		Panel *hot = child->HasHotkey(key);
		if (hot && hot->IsVisible() && hot->IsEnabled())
		{
			return hot;
		}
	}
	
	return NULL;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
Panel *FocusNavGroup::GetDefaultPanel()
{
	for (int i = 0; i < _mainPanel->GetChildCount(); i++)
	{
		Panel *child = _mainPanel->GetChild(i);
		if ( !child )
			continue;

		if (child->GetTabPosition() == 1)
		{
			return child;
		}
	}

	return NULL;	// no specific panel set
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
Panel *FocusNavGroup::GetCurrentFocus()
{
	return _currentFocus ? ipanel()->GetPanel(_currentFocus, vgui::GetControlsModuleName()) : NULL;
}

//-----------------------------------------------------------------------------
// Purpose: Sets the current focus
//-----------------------------------------------------------------------------
VPANEL FocusNavGroup::SetCurrentFocus(VPANEL focus, VPANEL defaultPanel)
{
	_currentFocus = focus;

    // if we haven't found a default panel yet, let's see if we know of one
    if (defaultPanel == NULL)
    {
        // can this focus itself by the default
        if (CanButtonBeDefault(focus))
        {
            defaultPanel = focus;
        }
        else if (_defaultButton)        // do we know of a default button
        {
            defaultPanel = _defaultButton;
        }
    }

    SetCurrentDefaultButton(defaultPanel);
    return defaultPanel;
}

//-----------------------------------------------------------------------------
// Purpose: Returns true if the specified panel can be the default
//-----------------------------------------------------------------------------
bool FocusNavGroup::CanButtonBeDefault(VPANEL panel)
{
	KeyValues *data = new KeyValues("CanBeDefaultButton");

	bool bResult = false;
	if (ipanel()->RequestInfo(panel, data))
	{
		bResult = (data->GetInt("result") == 1);
	}
	data->deleteThis();
	return bResult;
}