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

#include <vgui/IVGui.h>
#include <KeyValues.h>

#include <vgui_controls/BuildGroup.h>
#include <vgui_controls/Button.h>
#include <vgui_controls/Controls.h>
#include <vgui_controls/WizardPanel.h>
#include <vgui_controls/WizardSubPanel.h>

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

using namespace vgui;

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
WizardPanel::WizardPanel(Panel *parent, const char *panelName) : Frame(parent, panelName)
{
	_currentSubPanel = NULL;
	_currentData = new KeyValues("WizardData");
	_showButtons = true;


	SetSizeable(false);

	CreateButtons();
}

//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
WizardPanel::~WizardPanel()
{
	if (_currentData)
	{
		_currentData->deleteThis();
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::PerformLayout()
{
	BaseClass::PerformLayout();

	// resize the sub panel to fit in the Client area
	int x, y, wide, tall;
	GetClientArea(x, y, wide, tall);

	if (_currentSubPanel && _currentSubPanel->isNonWizardPanel())
	{
		// just have the subpanel cover the full size
		_currentSubPanel->SetBounds(x, y, wide, tall);
		_cancelButton->SetVisible(false);
		_prevButton->SetVisible(false);
		_nextButton->SetVisible(false);
		_finishButton->SetVisible(false);
	}
	else
	{
		// make room for the buttons at bottom
		if (_currentSubPanel) 
		{
			if( _showButtons )
			{
				_currentSubPanel->SetBounds(x, y, wide, tall - 35);
			}
			else
			{
				_currentSubPanel->SetBounds(x, y, wide, tall);
			}
		}

		// align the buttons to the right hand side
		GetSize(wide, tall);


		int bwide, btall;
		_cancelButton->GetSize(bwide, btall);
		
		x = wide - (20 + bwide);
		y = tall - (12 + btall);

		_cancelButton->SetPos(x, y);
		x -= (20 + bwide);

		// only display one of the next or finish buttons (and only if both are visible)
		if ( _showButtons )
		{
			if (_finishButton->IsEnabled() )
			{
				_nextButton->SetVisible(false);
				_finishButton->SetVisible(true);
				_finishButton->SetPos(x, y);
			}
			else
			{
				_nextButton->SetVisible(true);
				_finishButton->SetVisible(false);
				_nextButton->SetPos(x, y);
			}
		}

		x -= (1 + bwide);
		_prevButton->SetPos(x, y);

		ResetDefaultButton();
	}
}


//-----------------------------------------------------------------------------
// Purpose: if we don't show buttons then let the sub panel occupy the whole screen
//-----------------------------------------------------------------------------
void WizardPanel::GetClientArea(int &x, int &y, int &wide, int &tall)
{
	if( _showButtons )
	{
		BaseClass::GetClientArea( x, y, wide, tall );
	}
	else
	{
		x = 0;
		y = 0;
		GetSize( wide, tall );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::ApplySchemeSettings(IScheme *pScheme)
{
	BaseClass::ApplySchemeSettings(pScheme);
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::Run(WizardSubPanel *startPanel)
{
	// make sure we're set up and Run the first panel
	Activate();

	// skip over sub panels if they don't want to be displayed
	startPanel = FindNextValidSubPanel(startPanel);

	// show it
	ActivateNextSubPanel(startPanel);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::ActivateBuildMode()
{
	// no subpanel, no build mode
	if (!_currentSubPanel)
		return;

	_currentSubPanel->ActivateBuildMode();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::ResetDefaultButton()
{
	// work out which is the default button
	if (_nextButton->IsEnabled())
	{
		_nextButton->SetAsDefaultButton(true);
	}
	else if (_finishButton->IsEnabled())
	{
		_finishButton->SetAsDefaultButton(true);
	}
	else if (_prevButton->IsEnabled())
	{
		_prevButton->SetAsDefaultButton(true);
	}
	/* Don't ever set the cancel button as the default, as it is too easy for users to quit the wizard without realizing
	else if (_cancelButton->IsEnabled())
	{
		_cancelButton->SetAsDefaultButton(true);
	}
	*/
	
	// reset them all (this may not be necessary)
	_nextButton->InvalidateLayout();
	_prevButton->InvalidateLayout();
	_cancelButton->InvalidateLayout();
	_finishButton->InvalidateLayout();

	Repaint();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::ResetKeyFocus()
{
	// set the focus on the default
	FocusNavGroup &navGroup = GetFocusNavGroup();
	Panel *def = navGroup.GetDefaultPanel();
	if (def)
	{
		if (def->IsEnabled() && def->IsVisible())
		{
			def->RequestFocus();
		}
		else
		{
			def->RequestFocusNext();
		}
	}

	ResetDefaultButton();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::CreateButtons()
{
	_prevButton = new Button(this, "PrevButton", "");
	_nextButton = new Button(this, "NextButton", "");
	_cancelButton = new Button(this, "CancelButton", "");
	_finishButton = new Button(this, "FinishButton", "");

	_prevButton->SetCommand(new KeyValues("PrevButton"));
	_nextButton->SetCommand(new KeyValues("NextButton"));
	_cancelButton->SetCommand(new KeyValues("CancelButton"));
	_finishButton->SetCommand(new KeyValues("FinishButton"));

	SetNextButtonText(NULL);
	SetPrevButtonText(NULL);
	SetFinishButtonText(NULL);
	SetCancelButtonText(NULL);

	_prevButton->SetSize(82, 24);
	_nextButton->SetSize(82, 24);
	_cancelButton->SetSize(82, 24);
	_finishButton->SetSize(82, 24);
}

//-----------------------------------------------------------------------------
// Purpose: clears all previous history
//-----------------------------------------------------------------------------
void WizardPanel::ResetHistory()
{
	_subPanelStack.RemoveAll();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::ActivateNextSubPanel(WizardSubPanel *subPanel)
{
	// get rid of previous panel
	WizardSubPanel *prevPanel = _currentSubPanel;
	if (prevPanel && prevPanel->ShouldDisplayPanel())
	{
		// hide
		prevPanel->SetVisible(false);

		// push onto history stack
		_subPanelStack.AddElement(_currentSubPanel);
	}

	// reenable all buttons, returning them to their default state
	_prevButton->SetEnabled(true);
	_nextButton->SetEnabled(true);
	_cancelButton->SetEnabled(true);
	_finishButton->SetEnabled(true);
	if ( _showButtons ) 
	{
		_prevButton->SetVisible(true);
		_cancelButton->SetVisible(true);
	}

	// set up new subpanel
	_currentSubPanel = subPanel;
	_currentSubPanel->SetParent(this);
	_currentSubPanel->SetVisible(true);

	_currentSubPanel->SetWizardPanel(this);
	_currentSubPanel->OnDisplayAsNext();
	_currentSubPanel->OnDisplay();
	_currentSubPanel->InvalidateLayout(false);

	if (!prevPanel)
	{
		// no previous panel, so disable the back button
		_prevButton->SetEnabled(false);		
	}

	_currentSubPanel->RequestFocus();

	RecalculateTabOrdering();
	InvalidateLayout(false);
	Repaint();
}

//-----------------------------------------------------------------------------
// Purpose: Pops the last panel off the stack and runs it
//-----------------------------------------------------------------------------
void WizardPanel::ActivatePrevSubPanel()
{
	_currentSubPanel->SetVisible(false);

	WizardSubPanel *prevPanel = NULL;
	if (_subPanelStack.GetCount())
	{
		// check to see if we need to jump back to a previous sub panel
		WizardSubPanel *searchPanel = _currentSubPanel->GetPrevSubPanel();
		if (searchPanel && _subPanelStack.HasElement(searchPanel))
		{
			// keep poping the stack till we find it
			while (_subPanelStack.GetCount() && prevPanel != searchPanel)
			{
				prevPanel = _subPanelStack[_subPanelStack.GetCount() - 1];
				_subPanelStack.RemoveElementAt(_subPanelStack.GetCount() - 1);
			}
		}
		else
		{
			// just get the last one
			prevPanel = _subPanelStack[_subPanelStack.GetCount() - 1];
			_subPanelStack.RemoveElementAt(_subPanelStack.GetCount() - 1);
		}
	}

	if (!prevPanel)
	{
		ivgui()->DPrintf2("Error: WizardPanel::ActivatePrevSubPanel(): no previous panel to go back to\n");
		return;
	}

	// hide old panel
	_currentSubPanel->SetVisible(false);
	
	// reenable all buttons, returning them to their default state
	_prevButton->SetEnabled(true);
	_nextButton->SetEnabled(true);
	_cancelButton->SetEnabled(true);
	_finishButton->SetEnabled(true);

	// Activate new panel
	_currentSubPanel = prevPanel;
	_currentSubPanel->RequestFocus();
	_currentSubPanel->SetWizardPanel(this);
	_currentSubPanel->OnDisplayAsPrev();
	_currentSubPanel->OnDisplay();
	_currentSubPanel->InvalidateLayout(false);

	// show the previous panel, but don't Activate it (since it should show just what it was previously)
	_currentSubPanel->SetVisible(true);

	if (!_subPanelStack.GetCount())
	{
		// no previous panel, so disable the back button
		_prevButton->SetEnabled(false);		
	}

	RecalculateTabOrdering();
	InvalidateLayout(false);
	Repaint();
}

//-----------------------------------------------------------------------------
// Purpose: Sets up the new tab ordering
//-----------------------------------------------------------------------------
void WizardPanel::RecalculateTabOrdering()
{
	if (_currentSubPanel)
	{
		_currentSubPanel->SetTabPosition(1);
	}
	_prevButton->SetTabPosition(2);
	_nextButton->SetTabPosition(3);
	_finishButton->SetTabPosition(4);
	_cancelButton->SetTabPosition(5);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::SetNextButtonEnabled(bool state)
{
	if (_nextButton->IsEnabled() != state)
	{
		_nextButton->SetEnabled(state);
		InvalidateLayout(false);
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::SetPrevButtonEnabled(bool state)
{
	if (_prevButton->IsEnabled() != state)
	{
		_prevButton->SetEnabled(state);
		InvalidateLayout(false);
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::SetFinishButtonEnabled(bool state)
{
	if (_finishButton->IsEnabled() != state)
	{
		_finishButton->SetEnabled(state);
		InvalidateLayout(false);
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::SetCancelButtonEnabled(bool state)
{
	if (_cancelButton->IsEnabled() != state)
	{
		_cancelButton->SetEnabled(state);
		InvalidateLayout(false);
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::SetNextButtonVisible(bool state)
{
	_nextButton->SetVisible(state);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::SetPrevButtonVisible(bool state)
{
	_prevButton->SetVisible(state);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::SetFinishButtonVisible(bool state)
{
	_finishButton->SetVisible(state);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::SetCancelButtonVisible(bool state)
{
	_cancelButton->SetVisible(state);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::SetNextButtonText(const char *text)
{
	if (text)
	{
		_nextButton->SetText(text);
	}
	else
	{
		_nextButton->SetText("#WizardPanel_Next");
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::SetPrevButtonText(const char *text)
{
	if (text)
	{
		_prevButton->SetText(text);
	}
	else
	{
		_prevButton->SetText("#WizardPanel_Back");
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::SetFinishButtonText(const char *text)
{
	if (text)
	{
		_finishButton->SetText(text);
	}
	else
	{
		_finishButton->SetText("#WizardPanel_Finish");
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::SetCancelButtonText(const char *text)
{
	if (text)
	{
		_cancelButton->SetText(text);
	}
	else
	{
		_cancelButton->SetText("#WizardPanel_Cancel");
	}
}

//-----------------------------------------------------------------------------
// Purpose: Finds the next panel that wants to be shown
//-----------------------------------------------------------------------------
WizardSubPanel *WizardPanel::FindNextValidSubPanel(WizardSubPanel *currentPanel)
{
	// skip over sub panels if they don't want to be displayed
	while (currentPanel)
	{
		currentPanel->SetWizardPanel(this);
		if (currentPanel->ShouldDisplayPanel())
			break;
		
		// ok the panel wants to be skipped, so skip ahead
		currentPanel = currentPanel->GetNextSubPanel();
	}

	return currentPanel;
}

//-----------------------------------------------------------------------------
// Purpose: Advances to the next panel
//-----------------------------------------------------------------------------
void WizardPanel::OnNextButton()
{
	if (_currentSubPanel)
	{
		bool shouldAdvance = _currentSubPanel->OnNextButton();
		if (shouldAdvance)
		{
			WizardSubPanel *nextPanel = FindNextValidSubPanel(_currentSubPanel->GetNextSubPanel());

			if (nextPanel)
			{
				KeyValues *kv = new KeyValues("ActivateNextSubPanel");
				kv->SetPtr("panel", nextPanel);
				ivgui()->PostMessage(GetVPanel(), kv, GetVPanel());
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Retreats to the previous panel
//-----------------------------------------------------------------------------
void WizardPanel::OnPrevButton()
{
	bool shouldRetreat = true;
	if (_currentSubPanel)
	{
		shouldRetreat = _currentSubPanel->OnPrevButton();
	}

	if (shouldRetreat)
	{
		ActivatePrevSubPanel();
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::OnFinishButton()
{
	if (_currentSubPanel && _currentSubPanel->OnFinishButton())
	{
		// hide ourselves away
		SetVisible(false);

		// automatically delete ourselves if marked to do so
		if (IsAutoDeleteSet())
		{
			MarkForDeletion();
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WizardPanel::OnCancelButton()
{
	if (_currentSubPanel && _currentSubPanel->OnCancelButton())
	{
		// hide ourselves away
		BaseClass::OnClose();
		if (IsAutoDeleteSet())
		{
			MarkForDeletion();
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: command handler for catching escape key presses
//-----------------------------------------------------------------------------
void WizardPanel::OnCommand(const char *command)
{
	if (!stricmp(command, "Cancel"))
	{
		if (_cancelButton->IsEnabled())
		{
			_cancelButton->DoClick();
		}
	}
	else
	{
		BaseClass::OnCommand(command);
	}
}


//-----------------------------------------------------------------------------
// Purpose: Maps close button to cancel button
//-----------------------------------------------------------------------------
void WizardPanel::OnClose()
{
	if (_cancelButton->IsEnabled())
	{
		_cancelButton->DoClick();
	}
	else if (_finishButton->IsEnabled())
	{
		_finishButton->DoClick();
	}

	// don't chain back
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
KeyValues *WizardPanel::GetWizardData()
{
	return _currentData;
}


//-----------------------------------------------------------------------------
// Purpose: whether to show the next,prev,finish and cancel buttons
//-----------------------------------------------------------------------------
void WizardPanel::ShowButtons(bool state)
{ 
	_showButtons = state; 	// hide the wizard panel buttons
	SetNextButtonVisible( state );
	SetPrevButtonVisible( state );
	SetFinishButtonVisible( state );
	SetCancelButtonVisible( state );
}

//-----------------------------------------------------------------------------
// Purpose: filters close buttons
//-----------------------------------------------------------------------------
void WizardPanel::OnCloseFrameButtonPressed()
{
	// only allow close if the cancel button is enabled
	if (_cancelButton->IsEnabled())
	{
		BaseClass::OnCloseFrameButtonPressed();
	}
}

//-----------------------------------------------------------------------------
// Purpose: returns a page by name
//-----------------------------------------------------------------------------
WizardSubPanel *WizardPanel::GetSubPanelByName(const char *pageName)
{
	return dynamic_cast<WizardSubPanel *>(FindChildByName(pageName));
}
