//--------------------------------------------------------------------------------------------------------
//========= Copyright Valve Corporation, All rights reserved. ============//

#include "cbase.h"

#ifdef SERVER_USES_VGUI

#include "NavUI.h"
#include "filesystem.h"
#include "tier0/icommandline.h"
#include "vgui_gamedll_int.h"
#include "ienginevgui.h"
#include "IGameUIFuncs.h"
#include "fmtstr.h"
#include "NavMenu.h"
#include <vgui_controls/MenuButton.h>

#include "SelectionTool.h"
#include "MeshTool.h"
#include "AttributeTool.h"

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


using namespace vgui;
class CNavUIBasePanel;
class CNavUIToolPanel;

extern IGameUIFuncs *gameuifuncs;


//--------------------------------------------------------------------------------------------------------
static CNavUIBasePanel *s_navUIPanel = NULL;
CNavUIBasePanel *TheNavUI( void )
{
	return s_navUIPanel;
}


//--------------------------------------------------------------------------------------------------------
ConVar NavGUIRebuild( "nav_gui_rebuild", "0", FCVAR_CHEAT, "Rebuilds the nav ui windows from scratch every time they're opened" );


//--------------------------------------------------------------------------------------------------------
void CNavUIButton::LookupKey( void )
{
	if ( m_hideKey == BUTTON_CODE_INVALID )
		m_hideKey = (gameuifuncs) ? gameuifuncs->GetButtonCodeForBind( "nav_gui" ) : BUTTON_CODE_INVALID;
}



//--------------------------------------------------------------------------------------------------------
void CNavUIButton::OnKeyCodePressed( KeyCode code )
{
	LookupKey();

	if ( code == m_hideKey )
	{
		m_hidePressedTimer.Start();
		return;
	}

	BaseClass::OnKeyCodePressed( code );
}


//--------------------------------------------------------------------------------------------------------
void CNavUIButton::OnKeyCodeReleased( KeyCode code )
{
	LookupKey();

	if ( code == m_hideKey )
	{
		if ( m_hidePressedTimer.HasStarted() && m_hidePressedTimer.GetElapsedTime() < 0.5f )
		{
			s_navUIPanel->ToggleVisibility();
			m_hidePressedTimer.Invalidate();
		}
		return;
	}

	BaseClass::OnKeyCodeReleased( code );
}


//--------------------------------------------------------------------------------------------------------
void CNavUITextEntry::LookupKey( void )
{
	if ( m_hideKey == BUTTON_CODE_INVALID )
		m_hideKey = (gameuifuncs) ? gameuifuncs->GetButtonCodeForBind( "nav_gui" ) : BUTTON_CODE_INVALID;
}



//--------------------------------------------------------------------------------------------------------
void CNavUITextEntry::OnKeyCodePressed( KeyCode code )
{
	LookupKey();

	if ( code == m_hideKey )
	{
		m_hidePressedTimer.Start();
		return;
	}

	BaseClass::OnKeyCodePressed( code );
}


//--------------------------------------------------------------------------------------------------------
void CNavUITextEntry::OnKeyCodeReleased( KeyCode code )
{
	LookupKey();

	if ( code == m_hideKey )
	{
		if ( m_hidePressedTimer.HasStarted() && m_hidePressedTimer.GetElapsedTime() < 0.5f )
		{
			s_navUIPanel->ToggleVisibility();
			m_hidePressedTimer.Invalidate();
		}
		return;
	}

	BaseClass::OnKeyCodeReleased( code );
}


//--------------------------------------------------------------------------------------------------------
void CNavUIComboBox::LookupKey( void )
{
	if ( m_hideKey == BUTTON_CODE_INVALID )
		m_hideKey = (gameuifuncs) ? gameuifuncs->GetButtonCodeForBind( "nav_gui" ) : BUTTON_CODE_INVALID;
}



//--------------------------------------------------------------------------------------------------------
void CNavUIComboBox::OnKeyCodePressed( KeyCode code )
{
	LookupKey();

	if ( code == m_hideKey )
	{
		m_hidePressedTimer.Start();
		return;
	}

	BaseClass::OnKeyCodePressed( code );
}


//--------------------------------------------------------------------------------------------------------
void CNavUIComboBox::OnKeyCodeReleased( KeyCode code )
{
	LookupKey();

	if ( code == m_hideKey )
	{
		if ( m_hidePressedTimer.HasStarted() && m_hidePressedTimer.GetElapsedTime() < 0.5f )
		{
			s_navUIPanel->ToggleVisibility();
			m_hidePressedTimer.Invalidate();
		}
		return;
	}

	BaseClass::OnKeyCodeReleased( code );
}


//--------------------------------------------------------------------------------------------------------
void CNavUICheckButton::LookupKey( void )
{
	if ( m_hideKey == BUTTON_CODE_INVALID )
		m_hideKey = (gameuifuncs) ? gameuifuncs->GetButtonCodeForBind( "nav_gui" ) : BUTTON_CODE_INVALID;
}



//--------------------------------------------------------------------------------------------------------
void CNavUICheckButton::OnKeyCodePressed( KeyCode code )
{
	LookupKey();

	if ( code == m_hideKey )
	{
		m_hidePressedTimer.Start();
		return;
	}

	BaseClass::OnKeyCodePressed( code );
}


//--------------------------------------------------------------------------------------------------------
void CNavUICheckButton::OnKeyCodeReleased( KeyCode code )
{
	LookupKey();

	if ( code == m_hideKey )
	{
		if ( m_hidePressedTimer.HasStarted() && m_hidePressedTimer.GetElapsedTime() < 0.5f )
		{
			s_navUIPanel->ToggleVisibility();
			m_hidePressedTimer.Invalidate();
		}
		return;
	}

	BaseClass::OnKeyCodeReleased( code );
}


//--------------------------------------------------------------------------------------------------------
CNavUIBasePanel::CNavUIBasePanel() : vgui::Frame( NULL, "NavUI" )
{
	m_hideKey = BUTTON_CODE_INVALID;
	SetScheme( "SourceScheme" );
	LoadControlSettings( "Resource/UI/NavUI.res" );
	SetAlpha( 0 );
	SetMouseInputEnabled( false );
	SetSizeable( false );
	SetMoveable( false );
	SetCloseButtonVisible( false );
	SetTitleBarVisible( false );

	SetLeftClickAction( "", "" );

	m_hidden = false;
	m_toolPanel = NULL;
	m_selectionPanel = NULL;

	SetTitle( "", true);

	m_dragSelecting = m_dragUnselecting = false;

	MenuButton *menuButton = dynamic_cast< MenuButton * >(FindChildByName( "FileMenuButton" ));
	if ( menuButton )
	{
		NavMenu * menu = new NavMenu( menuButton, "NavFileMenu" );
		menu->AddMenuItem( "Quit", "Quit", new KeyValues( "Command", "command", "StopEditing" ), this );
		menuButton->SetMenu( menu );
		menuButton->SetOpenDirection( Menu::DOWN );
	}

	menuButton = dynamic_cast< MenuButton * >(FindChildByName( "SelectionMenuButton" ));
	if ( menuButton )
	{
		NavMenu * menu = new NavMenu( menuButton, "NavSelectionMenu" );
		menu->AddMenuItem( "Flood Select", "Flood Select", new KeyValues( "Command", "command", "FloodSelect" ), this );
		menu->AddMenuItem( "Flood Select (fog)", "Flood Select (Fog)", new KeyValues( "Command", "command", "FloodSelect fog" ), this );
		menuButton->SetMenu( menu );
		menuButton->SetOpenDirection( Menu::DOWN );
	}
}


//--------------------------------------------------------------------------------------------------------
CNavUIBasePanel::~CNavUIBasePanel()
{
	s_navUIPanel = NULL;
}


//--------------------------------------------------------------------------------------------------------
void CNavUIBasePanel::SetLeftClickAction( const char *action, const char *text )
{
	if ( !action || !*action )
	{
		action = "Selection::Select";
	}

	if ( !text || !*text )
	{
		text = "Select";
	}

	V_strncpy( m_leftClickAction, action, sizeof( m_leftClickAction ) );
	m_performingLeftClickAction = false;

	vgui::Label *label = dynamic_cast< vgui::Label * >(FindChildByName( "LeftClick" ) );
	if ( label )
	{
		label->SetText( UTIL_VarArgs( "Left Click: %s", text ) );
	}
}


//--------------------------------------------------------------------------------------------------------
void CNavUIBasePanel::ApplySchemeSettings( IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );
	SetBgColor( Color( 0, 0, 0, 0 ) );

	Panel *panel = FindChildByName( "SidebarParent" );
	if ( panel )
	{
		panel->SetBgColor( Color( 128, 128, 128, 255 ) );
		panel->SetPaintBackgroundType( 2 );
	}

	panel = FindChildByName( "MenuParent" );
	if ( panel )
	{
		panel->SetBgColor( Color( 128, 128, 128, 255 ) );
		panel->SetPaintBackgroundType( 0 );
	}

	panel = FindChildByName( "ToolParent" );
	if ( panel )
	{
		panel->SetBgColor( Color( 0, 0, 0, 128 ) );
		panel->SetPaintBackgroundType( 0 );
	}

	panel = FindChildByName( "SelectionParent" );
	if ( panel )
	{
		panel->SetBgColor( Color( 0, 0, 0, 128 ) );
		panel->SetPaintBackgroundType( 0 );
	}

	panel = FindChildByName( "MouseFeedbackParent" );
	if ( panel )
	{
		panel->SetBgColor( Color( 0, 0, 0, 128 ) );
		panel->SetPaintBackgroundType( 0 );
	}
}


//--------------------------------------------------------------------------------------------------------
void CNavUIBasePanel::PerformLayout( void )
{
	int wide, tall;
	vgui::surface()->GetScreenSize( wide, tall );
	SetBounds( 0, 0, wide, tall );

	Panel *panel = FindChildByName( "MenuParent" );
	if ( panel )
	{
		int oldWide, oldTall;
		panel->GetSize( oldWide, oldTall );
		panel->SetSize( wide, oldTall );
	}

	BaseClass::PerformLayout();
}


//--------------------------------------------------------------------------------------------------------
void CNavUIBasePanel::PaintBackground( void )
{
	BaseClass::PaintBackground();
}


//--------------------------------------------------------------------------------------------------------
const char *CNavUIBasePanel::ActiveToolName( void ) const
{
	if ( m_toolPanel )
		return m_toolPanel->GetName();

	return "";
}


//--------------------------------------------------------------------------------------------------------
void CNavUIBasePanel::ActivateTool( const char *toolName )
{
	if ( m_toolPanel && FStrEq( m_toolPanel->GetName(), toolName ) )
	{
		m_toolPanel->Shutdown();
		m_toolPanel->MarkForDeletion();
		m_toolPanel = NULL;
	}
	else
	{
		if ( m_toolPanel )
		{
			m_toolPanel->Shutdown();
			m_toolPanel->MarkForDeletion();
			m_toolPanel = NULL;
		}

		Panel *toolParent = FindChildByName( "ToolParent" );
		if ( !toolParent )
			toolParent = this;

		m_toolPanel = CreateTool( toolName, toolParent );
		if ( m_toolPanel )
		{
			m_toolPanel->Init();
			m_toolPanel->SetVisible( true );
		}
	}
}


//--------------------------------------------------------------------------------------------------------
CNavUIToolPanel *CNavUIBasePanel::CreateTool( const char *toolName, vgui::Panel *toolParent )
{
	if ( FStrEq( toolName, "Selection" ) )
	{
		return new SelectionToolPanel( toolParent, toolName );
	}
	if ( FStrEq( toolName, "Mesh" ) )
	{
		return new MeshToolPanel( toolParent, toolName );
	}
	if ( FStrEq( toolName, "Attribute" ) )
	{
		return new AttributeToolPanel( toolParent, toolName );
	}
	return NULL;
}


//--------------------------------------------------------------------------------------------------------
void CNavUIBasePanel::OnCommand( const char *command )
{
	CSplitString argv( command, " " );

	if ( FStrEq( "Close", command ) )
	{
		ToggleVisibility();
	}
	else if ( FStrEq( "MeshTool", command ) )
	{
		ActivateTool( "Mesh" );
		return;
	}
	else if ( FStrEq( "AttributesTool", command ) )
	{
		ActivateTool( "Attribute" );
		return;
	}
	else if ( FStrEq( "StopEditing", command ) )
	{
		MarkForDeletion();
		engine->ServerCommand( "nav_edit 0\n" );
	}
	else
	{
		BaseClass::OnCommand( command );
	}

	// argv can't delete individual elements
}


//--------------------------------------------------------------------------------------------------------
// GameUI panels are always visible by default, so here we hide ourselves if the GameUI is up.
void CNavUIBasePanel::OnTick( void )
{
	CBasePlayer *player = UTIL_GetListenServerHost();
	if ( !player || !player->IsConnected() )
	{
		m_hidden = true;
		SetVisible( false );
		vgui::ivgui()->RemoveTickSignal( GetVPanel() );
		return;
	}

	if ( enginevgui->IsGameUIVisible() )
	{
		if ( GetAlpha() != 0 )
		{
			SetAlpha( 0 );
			SetMouseInputEnabled( false );
		}
	}
	else
	{
		if ( m_hidden )
		{
			if ( GetAlpha() > 0 )
			{
				SetAlpha( 0 );
				SetMouseInputEnabled( false );
			}

			SetVisible( false );
			vgui::ivgui()->RemoveTickSignal( GetVPanel() );
			return;
		}
		else
		{
			if ( GetAlpha() < 255 )
			{
				SetAlpha( 255 );
				SetMouseInputEnabled( true );

				if ( !m_selectionPanel )
				{
					Panel *selectionParent = FindChildByName( "SelectionParent" );
					if ( !selectionParent )
						selectionParent = this;

					m_selectionPanel = CreateTool( "Selection", selectionParent );
					if ( m_selectionPanel )
					{
						m_selectionPanel->Init();
						m_selectionPanel->SetVisible( true );
					}
				}
			}

			CFmtStr str;
			if ( m_toolPanel )
			{
				str.sprintf( "%s - %s", STRING( gpGlobals->mapname ), m_toolPanel->GetName() );
			}
			else
			{
				str.sprintf( "%s", STRING( gpGlobals->mapname ) );
			}
			SetTitle( str.Access(), true );
		}
	}
}


//--------------------------------------------------------------------------------------------------------
void CNavUIBasePanel::ToggleVisibility( void )
{
	m_hidden = !m_hidden;

	if ( m_hidden && NavGUIRebuild.GetBool() )
	{
		MarkForDeletion();
		s_navUIPanel = NULL;
	}
}


//--------------------------------------------------------------------------------------------------------
Panel *CNavUIBasePanel::CreateControlByName( const char *controlName )
{
	if ( FStrEq( controlName, "Button" ) )
	{
		return new CNavUIButton( this, "CNavUIButton" );
	}

	if ( FStrEq( controlName, "TextEntry" ) )
	{
		return new CNavUITextEntry( this, "CNavUITextEntry" );
	}

	if ( FStrEq( controlName, "ComboBox" ) )
	{
		return new CNavUIComboBox( this, "CNavUIComboBox", 5, false );
	}

	if ( FStrEq( controlName, "CheckButton" ) )
	{
		return new CNavUICheckButton( this, "CNavUICheckButton", "" );
	}

	return BaseClass::CreateControlByName( controlName );
}


//--------------------------------------------------------------------------------------------------------
Panel *CNavUIToolPanel::CreateControlByName( const char *controlName )
{
	if ( s_navUIPanel )
	{
		return s_navUIPanel->CreateControlByName( controlName );
	}

	return BaseClass::CreateControlByName( controlName );
}


//--------------------------------------------------------------------------------------------------------
bool CNavUIToolPanel::IsCheckButtonChecked( const char *name )
{
	vgui::CheckButton *checkButton = dynamic_cast< vgui::CheckButton * >( FindChildByName( name, true ) );
	if ( !checkButton )
		return false;

	return checkButton->IsSelected();
}


//--------------------------------------------------------------------------------------------------------
void CNavUIBasePanel::LookupKey( void )
{
	if ( m_hideKey == BUTTON_CODE_INVALID )
		m_hideKey = (gameuifuncs) ? gameuifuncs->GetButtonCodeForBind( "nav_gui" ) : BUTTON_CODE_INVALID;
}



//--------------------------------------------------------------------------------------------------------
void CNavUIBasePanel::OnKeyCodePressed( KeyCode code )
{
	LookupKey();

	if ( code == m_hideKey )
	{
		m_hidePressedTimer.Start();
		return;
	}

	BaseClass::OnKeyCodePressed( code );
}


//--------------------------------------------------------------------------------------------------------
void CNavUIBasePanel::OnKeyCodeReleased( KeyCode code )
{
	LookupKey();

	if ( code == m_hideKey )
	{
		if ( m_hidePressedTimer.HasStarted() && m_hidePressedTimer.GetElapsedTime() < 0.5f )
		{
			s_navUIPanel->ToggleVisibility();
			m_hidePressedTimer.Invalidate();
		}
		return;
	}

	BaseClass::OnKeyCodeReleased( code );
}


//--------------------------------------------------------------------------------------------------------
void CNavUIBasePanel::OnCursorEntered( void )
{
	BaseClass::OnCursorEntered();

	if ( m_performingLeftClickAction )
	{
		if ( m_toolPanel )
		{
			m_toolPanel->FinishLeftClickAction( m_leftClickAction );
		}
		if ( m_selectionPanel )
		{
			m_selectionPanel->FinishLeftClickAction( m_leftClickAction );
		}
		m_performingLeftClickAction = false;
	}
}


//--------------------------------------------------------------------------------------------------------
void CNavUIBasePanel::OnCursorMoved( int x, int y )
{
	if ( m_toolPanel )
	{
		m_toolPanel->OnCursorMoved( x, y );
	}
	if ( m_selectionPanel )
	{
		m_selectionPanel->OnCursorMoved( x, y );
	}
	
	BaseClass::OnCursorMoved( x, y );
}


//--------------------------------------------------------------------------------------------------------
void CNavUIBasePanel::OnCursorExited( void )
{
	BaseClass::OnCursorExited();

	if ( m_performingLeftClickAction )
	{
		if ( m_toolPanel )
		{
			m_toolPanel->FinishLeftClickAction( m_leftClickAction );
		}
		if ( m_selectionPanel )
		{
			m_selectionPanel->FinishLeftClickAction( m_leftClickAction );
		}
		m_performingLeftClickAction = false;
	}
	/*
	if ( m_dragSelecting || m_dragUnselecting )
	{
		PlaySound( "EDIT_END_AREA.Creating" );
	}
	m_dragSelecting = false;
	m_dragUnselecting = false;
	*/
}


//--------------------------------------------------------------------------------------------------------
void CNavUIBasePanel::OnMousePressed( MouseCode code )
{
	/*
	CNavArea *area = TheNavMesh->GetSelectedArea();
	*/

	switch ( code )
	{
	case MOUSE_LEFT:
		m_performingLeftClickAction = true;
		if ( m_toolPanel )
		{
			m_toolPanel->StartLeftClickAction( m_leftClickAction );
		}
		if ( m_selectionPanel )
		{
			m_selectionPanel->StartLeftClickAction( m_leftClickAction );
		}
		break;
	}

	BaseClass::OnMousePressed( code );
}


//--------------------------------------------------------------------------------------------------------
void CNavUIBasePanel::OnMouseReleased( MouseCode code )
{
	switch ( code )
	{
	case MOUSE_LEFT:
		if ( m_performingLeftClickAction )
		{
			if ( m_toolPanel )
			{
				m_toolPanel->FinishLeftClickAction( m_leftClickAction );
			}
			if ( m_selectionPanel )
			{
				m_selectionPanel->FinishLeftClickAction( m_leftClickAction );
			}
			m_performingLeftClickAction = false;
		}
		break;
	case MOUSE_RIGHT:
		if ( m_toolPanel )
		{
			m_toolPanel->StartRightClickAction( "Selection::ClearSelection" );
		}
		if ( m_selectionPanel )
		{
			m_selectionPanel->StartRightClickAction( "Selection::ClearSelection" );
		}
		break;
	}

	BaseClass::OnMousePressed( code );
}


//--------------------------------------------------------------------------------------------------------
void CNavUIBasePanel::PlaySound( const char *sound )
{
	CBasePlayer *player = UTIL_GetListenServerHost();
	if ( player )
	{
		player->EmitSound( sound );
	}
}


//--------------------------------------------------------------------------------------------------------
// Taken from cl_dll/view.cpp
float ScaleFOVByWidthRatio( float fovDegrees, float ratio )
{
	float halfAngleRadians = fovDegrees * ( 0.5f * M_PI / 180.0f );
	float t = tan( halfAngleRadians );
	t *= ratio;
	float retDegrees = ( 180.0f / M_PI ) * atan( t );
	return retDegrees * 2.0f;
}


//--------------------------------------------------------------------------------------------------------
// Purpose:
// Given a field of view and mouse/screen positions as well as the current
// render origin and angles, returns a unit vector through the mouse position
// that can be used to trace into the world under the mouse click pixel.
// Input : 
// mousex -
// mousey -
// fov -
// vecRenderOrigin - 
// vecRenderAngles -
// Output :
// vecPickingRay
// Adapted from cl_dll/c_vguiscreen.cpp
//--------------------------------------------------------------------------------------------------------
void ScreenToWorld( int mousex, int mousey, float fov,
				   const Vector& vecRenderOrigin,
				   const QAngle& vecRenderAngles,
				   Vector& vecPickingRay )
{
	float dx, dy;
	float c_x, c_y;
	float dist;
	Vector vpn, vup, vright;

	int wide, tall;
	vgui::surface()->GetScreenSize( wide, tall );
	c_x = wide / 2;
	c_y = tall / 2;

	float scaled_fov = ScaleFOVByWidthRatio( fov, (float)wide / (float)tall * 0.75f );

	dx = (float)mousex - c_x;
	// Invert Y
	dy = c_y - (float)mousey;

	// Convert view plane distance
	dist = c_x / tan( M_PI * scaled_fov / 360.0 );

	// Decompose view angles
	AngleVectors( vecRenderAngles, &vpn, &vright, &vup );

	// Offset forward by view plane distance, and then by pixel offsets
	vecPickingRay = vpn * dist + vright * ( dx ) + vup * ( dy );

	// Convert to unit vector
	VectorNormalize( vecPickingRay );
} 


//--------------------------------------------------------------------------------------------------------
void GetNavUIEditVectors( Vector *pos, Vector *forward )
{
	CBasePlayer *player = UTIL_GetListenServerHost();
	if ( !player )
	{
		return;
	}

	if ( !s_navUIPanel )
	{
		return;
	}

	if ( s_navUIPanel->GetAlpha() < 255 )
	{
		return;
	}

	int x, y;
	vgui::surface()->SurfaceGetCursorPos( x, y );

	float fov = player->GetFOV();
	QAngle eyeAngles = player->EyeAngles();
	Vector eyePosition = player->EyePosition();

	Vector pick;
	ScreenToWorld( x, y, fov, eyePosition, eyeAngles, pick );

	*forward = pick;
}


//--------------------------------------------------------------------------------------------------------
void NavUICommand( void )
{
	if ( engine->IsDedicatedServer() )
		return;

	if ( UTIL_GetCommandClient() != UTIL_GetListenServerHost() )
		return;

	engine->ServerCommand( "nav_edit 1\n" );
	bool created = false;
	if ( !s_navUIPanel )
	{
		created = true;
		s_navUIPanel = CreateNavUI();
	}

	ShowGameDLLPanel( s_navUIPanel );
	vgui::ivgui()->AddTickSignal( s_navUIPanel->GetVPanel() );
	if ( !created )
	{
		s_navUIPanel->ToggleVisibility();
	}
}
ConCommand nav_gui( "nav_gui", NavUICommand, "Opens the nav editing GUI", FCVAR_CHEAT );

#endif // SERVER_USES_VGUI

//--------------------------------------------------------------------------------------------------------