//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ========
//
// Purpose: Implementation of the CScriptEditorPanel class and associated helper
// classes. The ScriptEditorPanel class represents a vgui panel which contains
// a text editing panel which may be used to edit a script and text panel which
// displays output from the script.
//
//=============================================================================

#include "toolutils/scripteditorpanel.h"
#include "vgui/iinput.h"
#include "vgui/ischeme.h"
#include "vgui/ivgui.h"
#include "vgui/isurface.h"
#include "vgui_controls/button.h"
#include "vgui_controls/textentry.h"
#include "vgui_controls/richtext.h"
#include "tier1/utlbuffer.h"
#include "vgui/ilocalize.h"

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

using namespace vgui;


//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CLineNumberPanel::CLineNumberPanel( vgui::Panel *pParent, vgui::TextEntry *pTextEntry, const char *pchName )
: BaseClass( pParent, pchName )
, m_pTextEntry( pTextEntry )
, m_hFont( NULL )
{

}


//-----------------------------------------------------------------------------
// Purpose: Paint the background of the panel, including the line numbers
//-----------------------------------------------------------------------------
void CLineNumberPanel::PaintBackground()
{
	BaseClass::PaintBackground();

	Panel *pParent = GetParent();
	if ( pParent )
	{
		if ( pParent->IsVisible() == false )
			return;
	}

	int width, height;
	GetSize( width, height );

	int fontHeight = surface()->GetFontTall( m_hFont );
	int yPos = 1;
	int line = m_pTextEntry->GetCurrentStartLine() + 1;

	surface()->DrawSetTextFont( m_hFont );
	surface()->DrawSetTextColor( m_Color );

	while ( (yPos + fontHeight) < height )
	{
		wchar_t sz[ 32 ];
		_snwprintf( sz, sizeof( sz ), L"%4i", line );

		surface()->DrawSetTextPos( 0, yPos );
		surface()->DrawPrintText( sz, wcslen( sz ) );

		yPos += fontHeight + 1;
		++line;
	}
}


//-----------------------------------------------------------------------------
// Purpose: Apply the settings from the provided scheme, and save the font to 
// display line numbers.
//-----------------------------------------------------------------------------
void CLineNumberPanel::ApplySchemeSettings(IScheme *pScheme)
{
	BaseClass::ApplySchemeSettings(pScheme);

	m_hFont = pScheme->GetFont( "ConsoleText", IsProportional() );
	m_Color = GetSchemeColor( "Console.TextColor", pScheme );
}




//-----------------------------------------------------------------------------
// Purpose: Constructor, creates all of the controls owned by the panel.
//-----------------------------------------------------------------------------
CScriptEditorPanel::CScriptEditorPanel( vgui::Panel *pParent, const char *pName ) 
: BaseClass( pParent, pName )
{
	SetKeyBoardInputEnabled( true );

	SetMinimumSize(300,200);

	// Create the output panel
	m_pOutput = new RichText(this, "ConsoleHistory");
	m_pOutput->SetAllowKeyBindingChainToParent( false );
	SETUP_PANEL( m_pOutput );
	m_pOutput->SetVerticalScrollbar( true );
	m_pOutput->GotoTextEnd();

	// Create the submit button
	m_pSubmit = new Button(this, "ConsoleSubmit", "#Console_Submit");
	m_pSubmit->SetCommand("submit");
	m_pSubmit->SetVisible( true );

	// Create the script entry panel
	m_pScriptEntry = new TextEntry(this, "ScriptEntry" );
	m_pScriptEntry->AddActionSignalTarget(this);
	m_pScriptEntry->SetMultiline( true );
	m_pScriptEntry->SetVerticalScrollbar( true );
	m_pScriptEntry->SetCatchEnterKey( true );
	m_pScriptEntry->SetCatchTabKey( true );
	m_pScriptEntry->SendNewLine( true );
	m_pScriptEntry->SetTabPosition(1);

	// Create the line number display panel
	m_pLineNumberPanel = new CLineNumberPanel( this, m_pScriptEntry, "ScriptLineNumbers" );
	
	// Set the default text colors, these will be overridden by ApplySchemeSettings
	m_PrintColor = Color(216, 222, 211, 255);
	m_DPrintColor = Color(196, 181, 80, 255);

	// Add to global console list so the the console 
	// output will be displayed in the output panel.
	g_pCVar->InstallConsoleDisplayFunc( this );
}


//-----------------------------------------------------------------------------
// Purpose: Destructor, removes the output from the global console list and
// destroys the controls.
//-----------------------------------------------------------------------------
CScriptEditorPanel::~CScriptEditorPanel()
{
	// Remove from the global console list
	g_pCVar->RemoveConsoleDisplayFunc( this );

	// Destroy the controls
	delete m_pOutput;
	m_pOutput = NULL;

	delete m_pSubmit;
	m_pSubmit = NULL;

	delete m_pScriptEntry;
	m_pScriptEntry = NULL;

	delete m_pLineNumberPanel;
	m_pLineNumberPanel = NULL;
}


//-----------------------------------------------------------------------------
// Purpose: Clears the output console
//-----------------------------------------------------------------------------
void CScriptEditorPanel::Clear()
{
	m_pOutput->SetText("");
	m_pOutput->GotoTextEnd();
}


//-----------------------------------------------------------------------------
// Purpose: color text print
//-----------------------------------------------------------------------------
void CScriptEditorPanel::ColorPrint( const Color& clr, const char *msg )
{
	m_pOutput->InsertColorChange( clr );
	m_pOutput->InsertString( msg );
}


//-----------------------------------------------------------------------------
// Purpose: normal text print
//-----------------------------------------------------------------------------
void CScriptEditorPanel::Print(const char *msg)
{
	ColorPrint( m_PrintColor, msg );
}


//-----------------------------------------------------------------------------
// Purpose: debug text print
//-----------------------------------------------------------------------------
void CScriptEditorPanel::DPrint( const char *msg )
{
	ColorPrint( m_DPrintColor, msg );
}


//-----------------------------------------------------------------------------
// Returns the console text
//-----------------------------------------------------------------------------
void CScriptEditorPanel::GetConsoleText( char *pchText, size_t bufSize ) const
{
	wchar_t *temp = new wchar_t[ bufSize ];
	m_pScriptEntry->GetText( temp, bufSize * sizeof( wchar_t ) );
	g_pVGuiLocalize->ConvertUnicodeToANSI( temp, pchText, bufSize );
	delete[] temp;
}

//-----------------------------------------------------------------------------
// Purpose: Called whenever the user types text
//-----------------------------------------------------------------------------
void CScriptEditorPanel::OnTextChanged(Panel *panel)
{
	if (panel != m_pScriptEntry)
		return;

	RequestFocus();
	m_pScriptEntry->RequestFocus();
}


//-----------------------------------------------------------------------------
// Purpose: generic vgui command handler
//-----------------------------------------------------------------------------
void CScriptEditorPanel::OnCommand(const char *command)
{
	if ( !Q_stricmp( command, "Submit" ) )
	{
		// always go the end of the buffer when the user has typed something
		m_pOutput->GotoTextEnd();

		// Get the text from the script entry and store in a single string.
		int textLength = m_pScriptEntry->GetTextLength() + 2;
		CUtlBuffer scriptBuffer( 0, textLength, CUtlBuffer::TEXT_BUFFER );
		m_pScriptEntry->GetText( (char*)scriptBuffer.Base(), scriptBuffer.Size() );
		
		// Run the script 
		RunScript( scriptBuffer );
	}
	else
	{
		BaseClass::OnCommand(command);
	}
}



//-----------------------------------------------------------------------------
// Purpose: Run the specified script 
//-----------------------------------------------------------------------------
void CScriptEditorPanel::RunScript( const CUtlBuffer& scriptBuffer )
{
	// No script execution exists at this level, derived classes should
	// override this function to execute scripts.
}


//-----------------------------------------------------------------------------
// Purpose: Determine of the script entry panel has the current focus
//-----------------------------------------------------------------------------
bool CScriptEditorPanel::TextEntryHasFocus() const
{
	return ( input()->GetFocus() == m_pScriptEntry->GetVPanel() );
}


//-----------------------------------------------------------------------------
// Purpose: Request that the script entry panel receive the input focus
//-----------------------------------------------------------------------------
void CScriptEditorPanel::TextEntryRequestFocus()
{
	m_pScriptEntry->RequestFocus();
}


//-----------------------------------------------------------------------------
// Purpose: Lays out the controls within the panel
//-----------------------------------------------------------------------------
void CScriptEditorPanel::PerformLayout()
{
	BaseClass::PerformLayout();

	// setup tab ordering
	GetFocusNavGroup().SetDefaultButton(m_pSubmit);

	IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
	m_pScriptEntry->SetBorder(pScheme->GetBorder("DepressedButtonBorder"));
	m_pOutput->SetBorder(pScheme->GetBorder("DepressedButtonBorder"));

	// layout controls
	int wide, tall;
	GetSize(wide, tall);

	const int lineNumberWidth = 24;
	const int inset = 8;
	const int topHeight = 4;
	const int entryInset = 4;
	const int submitWide = 64;
	const int submitHeight = 20;
	const int submitInset = 7; // x inset to pull the submit button away from the frame grab
	
	const int editHeight = ( tall - ( submitHeight + topHeight + (inset * 4 ) ) ) / 2;

	m_pOutput->SetPos(inset, inset + topHeight); 
	m_pOutput->SetSize(wide - (inset * 2), editHeight );
	m_pOutput->InvalidateLayout();

	int nSubmitXPos = wide - ( inset + submitWide + submitInset );
	m_pSubmit->SetPos( nSubmitXPos, tall - (entryInset * 2 + submitHeight ));
	m_pSubmit->SetSize( submitWide, submitHeight );

	int nScriptEntryYPos = topHeight + ( inset * 2 ) + editHeight;
	m_pScriptEntry->SetPos( ( inset * 2 ) + lineNumberWidth,  nScriptEntryYPos );
	m_pScriptEntry->SetSize( wide - ( (inset * 3) + lineNumberWidth ), editHeight );

	m_pLineNumberPanel->SetPos( inset, nScriptEntryYPos );
	m_pLineNumberPanel->SetSize( lineNumberWidth, editHeight );
}


//-----------------------------------------------------------------------------
// Purpose: Apply the color, font, and size settings from the specified scheme
//-----------------------------------------------------------------------------
void CScriptEditorPanel::ApplySchemeSettings( IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );

	m_PrintColor = GetSchemeColor( "Console.TextColor", pScheme );
	m_DPrintColor = GetSchemeColor( "Console.DevTextColor", pScheme );
	m_pOutput->SetFont( pScheme->GetFont( "ConsoleText", IsProportional() ) );
	m_pScriptEntry->SetFont( pScheme->GetFont( "ConsoleText", IsProportional() ) );

	InvalidateLayout();
}