//=========== Copyright Valve Corporation, All rights reserved. ===============//
//
// Purpose: 
//=============================================================================//

#ifndef PANORAMA_HTML_H
#define PANORAMA_HTML_H

#ifdef _WIN32
#pragma once
#endif

#include "panel2d.h"
#include "tier1/utlmap.h"
#include "tier1/utlstring.h"
#include "../uievent.h"
#include "mathlib/beziercurve.h"
#include "../textinput/textinput.h"

#if !defined( SOURCE2_PANORAMA ) && !defined( PANORAMA_PUBLIC_STEAM_SDK )
#include "html/ihtmlchrome.h"
#include "tier1/shared_memory.h"
#endif

class CTexturePanel;
namespace panorama
{
class CTextTooltip;
class CHTML;
class CFileOpenDialog;

struct HtmlFormHasFocus_t
{
	HtmlFormHasFocus_t() :
		m_bInput( false ),
		m_bInputHasMultiplePeers( false ),
		m_bUserInputThisPage( false ),
		m_bFocusedElementChanged( true )
	{
	}
	void Reset()
	{
		m_bInput = false;
		m_bUserInputThisPage = false;
		m_bInputHasMultiplePeers = false;
		m_bFocusedElementChanged = true;
	}

	bool operator==(const HtmlFormHasFocus_t &rhs) const
	{
		return rhs.m_bInput == m_bInput && 
			rhs.m_sName == m_sName &&
			rhs.m_sSearchLabel == m_sSearchLabel &&
			rhs.m_sInputType == m_sInputType;
	}
	bool			m_bInput;
	CUtlString		m_sName;
	CUtlString		m_sSearchLabel;
	bool			m_bInputHasMultiplePeers;
	bool			m_bUserInputThisPage;
	CUtlString		m_sInputType;
	bool			m_bFocusedElementChanged;
};

DECLARE_PANEL_EVENT2( HTMLURLChanged, const char *, const char * )
DECLARE_PANEL_EVENT1( HTMLLoadPage, const char * )
DECLARE_PANEL_EVENT2( HTMLFinishRequest, const char *, const char * )
DECLARE_PANEL_EVENT1( HTMLTitle, const char * )
DECLARE_PANEL_EVENT1( HTMLStatusText, const char * )
DECLARE_PANEL_EVENT2( HTMLJSAlert, const char *, bool * )
DECLARE_PANEL_EVENT2( HTMLJSConfirm, const char *, bool * )
DECLARE_PANEL_EVENT2( HMTLLinkAtPosition, const char *, bool )
DECLARE_PANEL_EVENT4( HMTLThumbNailImage, int, CUtlBuffer *, uint32, uint32  )
DECLARE_PANEL_EVENT1( HTMLOpenLinkInNewTab, const char * )
DECLARE_PANEL_EVENT2( HTMLOpenPopupTab,  CHTML *, const char * )
DECLARE_PANEL_EVENT2( HTMLBackForwardState, bool, bool )
DECLARE_PANEL_EVENT2( HTMLUpdatePageSize, int, int )
DECLARE_PANEL_EVENT5( HTMLSecurityStatus, const char *, bool, bool, bool, const char * )
DECLARE_PANEL_EVENT1( HTMLFullScreen, bool )
DECLARE_PANEL_EVENT2( HTMLStartMousePanning, int, int )
DECLARE_PANEL_EVENT0( HTMLStopMousePanning )
DECLARE_PANEL_EVENT0( HTMLCloseWindow )
DECLARE_PANEL_EVENT2( HTMLFormHasFocus, HtmlFormHasFocus_t, const char * /* URL */ )
DECLARE_PANEL_EVENT2( HTMLScreenShotTaken, const char *, const char * )
DECLARE_PANEL_EVENT1( HTMLFocusedNodeValue, const char * )
DECLARE_PANEL_EVENT0( HTMLSteamRightPadMoving );
DECLARE_PANEL_EVENT2( HTMLStartRequest, const char *, bool * );

class CImagePanel;

enum CursorCode
{
	eCursorNone,
	eCursorArrow
};

class IUIDoubleBufferedTexture;
class CTransform3D;
extern const int k_nExtraScrollRoom; // max number of padding pixels to use if needed

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CHTML : public CPanel2D, public ITextInputControl
#if !defined( SOURCE2_PANORAMA ) && !defined( PANORAMA_PUBLIC_STEAM_SDK )
	, public IHTMLResponses
#endif
{
	DECLARE_PANEL2D( CHTML, CPanel2D );

public:
	CHTML( CPanel2D *parent, const char * pchPanelID,  bool bPopup = false );
	virtual ~CHTML();
	void Shutdown();

	// panel2d overrides
	virtual void Paint();
	virtual bool BSetProperty( CPanoramaSymbol symName, const char *pchValue ) OVERRIDE;
	virtual void OnLayoutTraverse( float flFinalWidth, float flFinalHeight );
	virtual void OnStylesChanged();
	virtual bool BRequiresContentClipLayer() OVERRIDE { return true; } // BUGBUG Alfred - fix ::Paint to scale u/v offsets rather than requiring a clipping

	// simple browser management
	void OpenURL(const char *);
	void PostURL( const char *pchURL, const char *pchPostData );
	void AddHeader( const char *pchHeader, const char *pchValue );
	void StopLoading();
	void Refresh();
	void GoBack();
	void GoForward();
	bool BCanGoBack();
	bool BCanGoForward();
	
	// kb/mouse management
	virtual bool OnKeyDown( const KeyData_t &code ) OVERRIDE;
	virtual bool OnKeyUp( const KeyData_t & code ) OVERRIDE;
	virtual bool OnKeyTyped( const KeyData_t &unichar ) OVERRIDE;
	virtual bool OnGamePadDown( const GamePadData_t &code ) OVERRIDE;
	virtual bool OnGamePadUp( const GamePadData_t &code ) OVERRIDE;
	virtual bool OnGamePadAnalog( const GamePadData_t &code ) OVERRIDE;
	virtual bool OnMouseButtonDown( const MouseData_t &code ) OVERRIDE;
	virtual bool OnMouseButtonUp( const MouseData_t &code ) OVERRIDE;
	virtual bool OnMouseButtonDoubleClick( const MouseData_t &code ) OVERRIDE;
	virtual bool OnMouseWheel( const MouseData_t &code ) OVERRIDE;
	virtual void OnMouseMove( float flMouseX, float flMouseY ) OVERRIDE;

	virtual void SetupJavascriptObjectTemplate() OVERRIDE;

	// run input event processing for something that may not be a real input event, so don't bubble to parents, etc.
	bool OnGamePadDownImpl( const GamePadData_t &code, bool *out_pbOptionalResult = nullptr );
	bool OnGamePadAnalogImpl( const GamePadData_t &code, bool *out_pbOptionalResult = nullptr );
	
	bool ProcessAnalogScroll( float fValueX, float fValueY, double fTimeDelta, float fDeadzoneValue );
	bool ProcessAnalogZoom( float fValueX, float fValueY, double fTimeDelta, float fDeadzoneValue );
	void ProcessRawScroll( bool bFingerDown );
	void ProcessRawZoom( float fValueRaw );

	// browser helpers
	void Copy();
	void Paste();
	void RequestLinkUnderGamepad() { RequestLinkAtPosition( GetActualLayoutWidth()/2 - GetHScrollOffset(), GetActualLayoutHeight()/2 - GetVScrollOffset() ); }
	void RequestLinkUnderMouse() { RequestLinkAtPosition( m_flCursorX - GetHScrollOffset(), m_flCursorY - GetVScrollOffset() ); }
	void ZoomToElementUnderPanelCenter();
	void ZoomToElementUnderMouse();
	const char *PchLastLinkAtPosition() { return m_LinkAtPos.m_sURL; }
	void RunJavascript( const char *pchScript );
	void ViewSource();
	void SetHorizontalScroll( int scroll );
	void SetVerticalScroll( int scroll );
	void OnHTMLCursorMove( float flMouseX, float flMouseY );

	// finding text on the page
	void Find( const char *pchSubStr );
	void StopFind();
	void FindNext();
	void FindPrevious();

	void SetFileDialogChoice( const char *pchFileName ); // callback if cef wanted us to pick a file

	bool BAcceptMouseInput(); // returns true if the control is listening to mouse input right now, false if gamepad input mode is on
	virtual bool BRequiresFocus() OVERRIDE { return true; }

	bool BIgnoreMouseBackForwardButtons() { return m_bIgnoreMouseBackForwardButtons; }
	void SetIgnoreMouseBackForwardButtons( bool bIgnore ) { m_bIgnoreMouseBackForwardButtons = bIgnore; }

	const char *PchCurrentURL() { return m_sCurrentURL; } // the current URL the browser has loaded
	const char *PchCurrentPageTitle() { return m_sHTMLTitle; } // the title of the currently loaded page

	void SaveCurrentPageToJPEG( const char *pchFileName, int nWide, int nTall ); // save this current page to a jpeg

	// results for JS alert popups
	void DismissJSDialog( bool bRetVal );

	static uint32 GetAndResetPaintCounter();

	void ReleaseTextureMemory( bool bSuppressTextureLoads = false );
	void RefreshTextureMemory();

	void CaptureThumbNailImage( CPanel2D *pEventTarget, int iUserData );
	void IncrementPageScale( float flScaleIncrement, bool bZoomFromOrigin = false );
	void ExitFullScreen();
	void ExecuteJavaScript( const char *pchScript );

	// SSL/security state for the loaded html page
	bool BIsSecure() const { return m_bIsSecure; }
	bool BIsCertError() const { return m_bIsCertError; }
	bool BIsEVCert() const { return m_bIsEVCert; }
	const char *PchCertName() const { return m_sCertName; }

	// if true don't allow the page to scroll beyond the page edges
	void SetDontAllowOverScroll( bool bState );
	void SetEmbeddedMode( bool bState );

	void ZoomPageToFocusedElement( int nLeftOffset, int nTopOffset );

	// ITextInputControl helpers
	virtual int32 GetCursorOffset() const { return 0; }
	virtual  uint GetCharCount() const { return 0; }

	virtual const char *PchGetText() const { return ""; }
	virtual const wchar_t *PwchGetText() const { return L""; }

	virtual void InsertCharacterAtCursor( const wchar_t &unichar );
	virtual void InsertCharactersAtCursor( const wchar_t *pwch, size_t cwch ) 
	{
		for ( uint i = 0; i < cwch; i++ )
			InsertCharacterAtCursor( pwch[i] ); 
	}
	bool BSupportsImmediateTextReturn() { return false; }
	void RequestControlString() { RequestFocusedNodeValue(); }

	virtual CPanel2D *GetAssociatedPanel() { return this; }

	void PauseFlashVideoIfVisible();

	void ResetScrollbarsAndClearOverflow();

	void SetPopupChild(CHTML *pChild) { m_pPopupChild = pChild; }

#ifdef DBGFLAG_VALIDATE
	virtual void ValidateClientPanel( CValidator &validator, const char *pchName ) OVERRIDE;
	static void ValidateStatics( CValidator &validator, const char *pchName );
#endif

	class CHTMLVerticalScrollBar : public CScrollBar
	{
		DECLARE_PANEL2D( CHTMLVerticalScrollBar, CScrollBar );

	public:
		CHTMLVerticalScrollBar( CPanel2D *parent, const char * pchPanelID ) : CScrollBar( parent, pchPanelID )
		{
			m_pScrollThumb->AddClass( "VerticalScrollThumb" );
		}

		void ScrollToMousePos()
		{
			float flHeight = GetActualLayoutHeight();
			if ( flHeight > 0.00001f )
			{
				if ( m_bMouseWentDownOnThumb )
				{
					float flPercentDiff = (m_flMouseY - m_flMouseStartY) / flHeight;
					float flPositionOffset = flPercentDiff * GetRangeSize();
					float flPosition = m_flScrollStartPosition + flPositionOffset;
					SetScrollWindowPosition( clamp( flPosition, 0.0f, GetRangeSize() - GetScrollWindowSize() ), true );
				}
				else
				{
					float flPercent = m_flMouseY / flHeight;
					float flPos = GetRangeSize() * flPercent;
					SetScrollWindowPosition( clamp( flPos, 0.0f, GetRangeSize() - GetScrollWindowSize() ), true );
				}
			}
		}


		virtual ~CHTMLVerticalScrollBar() {}

	protected:
		virtual void UpdateLayout( bool bImmediateMove )
		{
			CUILength zero;
			zero.SetLength( 0.0f );

			if ( GetRangeSize() < 0.001f )
				return;

			CUILength length;

			float flXPosPercent = (GetScrollWindowPosition() - GetRangeMin()) / GetRangeSize();
			length.SetPercent( flXPosPercent * 100.0f );
			if ( bImmediateMove )
				m_pScrollThumb->SetPositionWithoutTransition( zero, length, zero );
			else
				m_pScrollThumb->SetPosition( zero, length, zero );

			float flWidthPercent = GetScrollWindowSize() / GetRangeSize();

			length.SetPercent( flWidthPercent*100.0f );
			m_pScrollThumb->AccessStyleDirty()->SetHeight( length );
			length.SetPercent( 100.0f );
			m_pScrollThumb->AccessStyleDirty()->SetWidth( length );

			m_bLastMoveImmediate = bImmediateMove;
		}
	};

	class CHTMLHorizontalScrollBar : public CScrollBar
	{
		DECLARE_PANEL2D( CHTMLHorizontalScrollBar, CScrollBar );

	public:
		CHTMLHorizontalScrollBar( CPanel2D *parent, const char * pchPanelID ) : CScrollBar( parent, pchPanelID )
		{
			m_pScrollThumb->AddClass( "HorizontalScrollThumb" );
		}

		void ScrollToMousePos()
		{
			float flWidth = GetActualLayoutWidth();
			if ( flWidth > 0.00001f )
			{
				if ( m_bMouseWentDownOnThumb )
				{
					float flPercentDiff = (m_flMouseX - m_flMouseStartX) / flWidth;
					float flPositionOffset = flPercentDiff * GetRangeSize();
					float flPosition = m_flScrollStartPosition + flPositionOffset;
					SetScrollWindowPosition( clamp( flPosition, 0.0f, GetRangeSize() - GetScrollWindowSize() ), true );
				}
				else
				{
					float flPercent = m_flMouseX / flWidth;
					float flPos = GetRangeSize() * flPercent;
					SetScrollWindowPosition( clamp( flPos, 0.0f, GetRangeSize() - GetScrollWindowSize() ), true );
				}
			}
		}


		virtual ~CHTMLHorizontalScrollBar() {}

	protected:
		virtual void UpdateLayout( bool bImmediateMove )
		{
			CUILength zero;
			zero.SetLength( 0.0f );

			if ( GetRangeSize() < 0.001f )
				return;

			CUILength length;

			float flXPosPercent = (GetScrollWindowPosition() - GetRangeMin()) / GetRangeSize();
			length.SetPercent( flXPosPercent * 100.0f );
			if ( bImmediateMove )
				m_pScrollThumb->SetPositionWithoutTransition( length, zero, zero );
			else
				m_pScrollThumb->SetPosition( length, zero, zero );

			float flWidthPercent = GetScrollWindowSize() / GetRangeSize();

			length.SetPercent( flWidthPercent*100.0f );
			m_pScrollThumb->AccessStyleDirty()->SetWidth( length );
			length.SetPercent( 100.0f );
			m_pScrollThumb->AccessStyleDirty()->SetHeight( length );
			m_bLastMoveImmediate = bImmediateMove;
		}
	};

	enum EHTMLScrollDirection
	{
		kHTMLScrollDirection_Up,
		kHTMLScrollDirection_Down,
		kHTMLScrollDirection_Left,
		kHTMLScrollDirection_Right
	};

	bool BCanScrollInDirection( EHTMLScrollDirection eDirection ) const;

	static float GetScrollDeadzoneScale() { return s_fScrollDeadzoneScale; }

protected:
	// functions you can override to specialize html behavior
	virtual void OnURLChanged( const char *url, const char *pchPostData, bool bIsRedirect );
	virtual void OnFinishRequest(const char *url, const char *pageTitle);
	virtual void OnPageLoaded( const char *url, const char *pageTitle, const CUtlMap < CUtlString, CUtlString > &headers );
	virtual bool OnStartRequestInternal( const char *url, const char *target, const char *pchPostData, bool bIsRedirect );
	virtual void ShowPopup();
	virtual void HidePopup();
	virtual bool OnOpenNewTab( const char *pchURL, bool bForeground );
	virtual bool OnPopupHTMLWindow( const char *pchURL, int x, int y, int wide, int tall );
	virtual void SetHTMLTitle( const char *pchTitle );
	virtual void OnLoadingResource( const char *pchURL );
	virtual void OnSetStatusText(const char *text);
	virtual void OnSetCursor( CursorCode cursor );
	virtual void OnFileLoadDialog( const char *pchTitle, const char *pchInitialFile );
	virtual void OnShowToolTip( const char *pchText );
	virtual void OnUpdateToolTip( const char *pchText );
	virtual void OnHideToolTip();
	virtual void OnSearchResults( int iActiveMatch, int nResults );

	friend class ::CTexturePanel;
	IUIDoubleBufferedTexture *m_pDoubleBufferedTexture;
	IUIDoubleBufferedTexture *m_pDoubleBufferedTexturePending;
	IUIDoubleBufferedTexture *m_pDoubleBufferedTextureComboBox;
	int32 m_nTextureSerial; // serial number of the last texture we uploaded

	void RequestFocusedNodeValue();
	// if true then our html control overrides scrolling and scrollbars, if false we 
	// let the web control scroll itself
	void SetManualHTMLScroll( bool bControlScroll ) { m_bControlPageScrolling = bControlScroll;  }

private:
	typedef void (CHTML::* ScrollFunc_t)( float, bool );
	bool ProcessAnalogScrollAxis( float fValue, float fDeadzoneValue, double fTimeDelta, ScrollFunc_t ScrollFunc );

	// we used to create the virtual mouse in our constructor, but now we don't know enough information at
	// construction time to know whether we want one (ie., if we're wrapped by CHTMLSimpleNavigationWrapper
	// we disable touchpad navigation). Instead we just try to lazy-create one at first use.
	void LazyCreateVirtualMouseIfNecessary();

	// getters/setters for html cef object
	void SetHTMLFocus();
	void KillHTMLFocus();
	int HorizontalScroll();
	int VerticalScroll();
	bool IsHorizontalScrollBarVisible();
	bool IsVeritcalScrollBarVisible();
	void RequestLinkAtPosition( int x, int y );
	void GetCookiesForURL( const char *pchURL );
	void UpdatePanoramaScrollBars();
	bool BHandleKeyPressPageScroll() const;

#if defined( SOURCE2_PANORAMA ) || defined( PANORAMA_PUBLIC_STEAM_SDK )
	STEAM_CALLBACK( CHTML, OnLinkAtPositionResponse, HTML_LinkAtPosition_t, m_LinkAtPosRespose );

	STEAM_CALLBACK( CHTML, OnHTMLNeedsPaint, HTML_NeedsPaint_t, m_HTML_NeedsPaint );
	STEAM_CALLBACK( CHTML, OnHTMLStartRequest, HTML_StartRequest_t, m_HTML_StartRequest );
	STEAM_CALLBACK( CHTML, OnHTMLCloseBrowser, HTML_CloseBrowser_t, m_HTML_CloseBrowser );
	STEAM_CALLBACK( CHTML, OnHTMLURLChanged, HTML_URLChanged_t, m_HTML_URLChanged );
	STEAM_CALLBACK( CHTML, OnHTMLFinishedRequest, HTML_FinishedRequest_t, m_HTML_FinishedRequest );
	STEAM_CALLBACK( CHTML, OnHTMLOpenLinkInNewTab, HTML_OpenLinkInNewTab_t, m_HTML_OpenLinkInNewTab );
	STEAM_CALLBACK( CHTML, OnHTMLChangedTitle, HTML_ChangedTitle_t, m_HTML_ChangedTitle );
	STEAM_CALLBACK( CHTML, OnHTMLSearchResults, HTML_SearchResults_t, m_HTML_SearchResults );
	STEAM_CALLBACK( CHTML, OnHTMLCanGoBackAndForward, HTML_CanGoBackAndForward_t, m_HTML_CanGoBackAndForward );
	STEAM_CALLBACK( CHTML, OnHTMLHorizontalScroll, HTML_HorizontalScroll_t, m_HTML_HorizontalScroll );
	STEAM_CALLBACK( CHTML, OnHTMLVerticalScroll, HTML_VerticalScroll_t, m_HTML_VerticalScroll );
	STEAM_CALLBACK( CHTML, OnHTMLJSAlert, HTML_JSAlert_t, m_HTML_JSAlert );
	STEAM_CALLBACK( CHTML, OnHTMLJSConfirm, HTML_JSConfirm_t, m_HTML_JSConfirm );
	STEAM_CALLBACK( CHTML, OnHTMLFileOpenDialog, HTML_FileOpenDialog_t, m_HTML_FileOpenDialog );
	STEAM_CALLBACK( CHTML, OnHTMLNewWindow, HTML_NewWindow_t, m_HTML_NewWindow );
	STEAM_CALLBACK( CHTML, OnHTMLSetCursor, HTML_SetCursor_t, m_HTML_SetCursor );
	STEAM_CALLBACK( CHTML, OnHTMLStatusText, HTML_StatusText_t, m_HTML_StatusText );
	STEAM_CALLBACK( CHTML, OnHTMLShowToolTip, HTML_ShowToolTip_t, m_HTML_ShowToolTip );
	STEAM_CALLBACK( CHTML, OnHTMLUpdateToolTip, HTML_UpdateToolTip_t, m_HTML_UpdateToolTip );
	STEAM_CALLBACK( CHTML, OnHTMLHideToolTip, HTML_HideToolTip_t, m_HTML_HideToolTip );
#else
	// message handlers for ipc thread
	void BrowserSetIndex( int idx ) { m_iBrowser = idx; SendPendingHTMLMessages(); }
	int  BrowserGetIndex() { return m_iBrowser; }
	void BrowserReady( const CMsgBrowserReady *pCmd );
	void BrowserSetSharedPaintBuffers( const CMsgSetSharedPaintBuffers *pCmd );
	void BrowserNeedsPaint( const CMsgNeedsPaint *pCmd );
	void BrowserStartRequest( const CMsgStartRequest *pCmd );
	void BrowserURLChanged( const CMsgURLChanged *pCmd );
	void BrowserLoadedRequest( const CMsgLoadedRequest *pCmd );
	void BrowserFinishedRequest(const CMsgFinishedRequest *pCmd);
	void BrowserPageSecurity( const CMsgPageSecurity *pCmd );
	void BrowserShowPopup( const CMsgShowPopup *pCmd );
	void BrowserHidePopup( const CMsgHidePopup *pCmd );
	void BrowserOpenNewTab( const CMsgOpenNewTab *pCmd );
	IHTMLResponses *BrowserPopupHTMLWindow( const CMsgPopupHTMLWindow *pCmd );
	void BrowserSetHTMLTitle( const CMsgSetHTMLTitle *pCmd );
	void BrowserLoadingResource( const CMsgLoadingResource *pCmd );
	void BrowserStatusText( const CMsgStatusText *pCmd );
	void BrowserSetCursor( const CMsgSetCursor *pCmd );
	void BrowserFileLoadDialog( const CMsgFileLoadDialog *pCmd );
	void BrowserShowToolTip( const CMsgShowToolTip *pCmd );
	void BrowserUpdateToolTip( const CMsgUpdateToolTip *pCmd );
	void BrowserHideToolTip( const CMsgHideToolTip *pCmd );
	void BrowserSearchResults( const CMsgSearchResults *pCmd );
	void BrowserClose( const CMsgClose *pCmd );
	void BrowserHorizontalScrollBarSizeResponse( const CMsgHorizontalScrollBarSizeResponse *pCmd );
	void BrowserVerticalScrollBarSizeResponse( const CMsgVerticalScrollBarSizeResponse *pCmd );
	void BrowserGetZoomResponse( const CMsgGetZoomResponse *pCmd ) {}
	void BrowserLinkAtPositionResponse( const CMsgLinkAtPositionResponse *pCmd );
	void BrowserZoomToElementAtPositionResponse( const CMsgZoomToElementAtPositionResponse *pCmd );
	void BrowserJSAlert( const CMsgJSAlert *pCmd );
	void BrowserJSConfirm( const CMsgJSConfirm *pCmd );
	void BrowserCanGoBackandForward( const CMsgCanGoBackAndForward *pCmd );
	void BrowserOpenSteamURL( const CMsgOpenSteamURL *pCmd );
	void BrowserSizePopup( const CMsgSizePopup *pCmd );
	void BrowserScalePageToValueResponse( const CMsgScalePageToValueResponse *pCmd );
	void BrowserRequestFullScreen( const CMsgRequestFullScreen *pCmd );
	void BrowserExitFullScreen( const CMsgExitFullScreen *pCmd );
	void BrowserGetCookiesForURLResponse( const CMsgGetCookiesForURLResponse *pCmd );
	void BrowserNodeGotFocus( const CMsgNodeHasFocus *pCmd );
	void BrowserSavePageToJPEGResponse( const CMsgSavePageToJPEGResponse *pCmd );
	void BrowserFocusedNodeValueResponse( const CMsgFocusedNodeTextResponse *pCmd );
	void BrowserComboNeedsPaint(const CMsgComboNeedsPaint *pCmd);
	bool BSupportsOffMainThreadPaints();
	void ThreadNotifyPendingPaints();
#endif

	// helpers to control browser side, pos and textures
	void SetBrowserSize( int wide, int tall );
	void SendPendingHTMLMessages();

	// helpers to move the html page around inside the control
	void ScrollPageUp( float flScrollValue, bool bApplyBezier );
	void ScrollPageDown( float flScrollValue, bool bApplyBezier );
	void ScrollPageLeft( float flScrollValue, bool bApplyBezier );
	void ScrollPageRight( float flScrollValue, bool bApplyBezier );

	// Overrides for scroll bar to call back to us rather than normal panel2d call
	virtual void ScrollToXPercent( float flXPercent );
	virtual void ScrollToYPercent( float flXPercent );

	// event handlers
	bool OnGamepadInput();
	bool OnPropertyTransitionEnd( const CPanelPtr< IUIPanel > &pPanel, CStyleSymbol prop );
	bool OnSetBrowserSize( const CPanelPtr< IUIPanel > &pPanel, int nWide, int nTall );
	bool OnHTMLFormFocusPending( const CPanelPtr< IUIPanel > &pPanel );
	bool OnInputFocusSet( const panorama::CPanelPtr< panorama::IUIPanel > &ptrPanel );
	bool OnInputFocusLost( const panorama::CPanelPtr< panorama::IUIPanel > &ptrPanel );
	bool OnHTMLScreenShotCaptured( const panorama::CPanelPtr< panorama::IUIPanel > &ptrPanel, int nThumbNailWidth, int nThumbNailHeight  );
	bool OnHTMLCommitZoom( const panorama::CPanelPtr< panorama::IUIPanel > &ptrPanel, float flZoom );
	bool OnHTMLRequestRepaint( const panorama::CPanelPtr< panorama::IUIPanel > &ptrPanel );

#if defined( SOURCE2_PANORAMA ) || defined( PANORAMA_PUBLIC_STEAM_SDK )
	bool OnFileOpenDialogFilesSelected( const panorama::CPanelPtr< panorama::IUIPanel > &ptrPanel, const char *pszFiles );
#endif

	// moving the html texture around
	void ResizeBrowserTextureIfNeeded();
	int AdjustPageScrollForTextureOffset( int &nTargetValue, const int nCurScroll, const int nMaxScroll, float &flOffsetTextureScroll, const float flMaxTextureScroll );

	int GetHScrollOffset() 
	{ 
		return m_ScrollLeft.m_flOffsetTextureScroll;
	}

	int GetVScrollOffset() 
	{ 
		return m_ScrollUp.m_flOffsetTextureScroll;
	}

	void ClampTextureScroll( bool bAllowScrollBorder = true );

	bool m_bInitialized; // used to prevent double shutdown
	bool m_bReady; // When we are ready to load a url
	CTexturePanel *m_pTexurePanel;

	int m_nWindowWide, m_nWindowTall; // how big the html texture should be
	int m_nTextureWide, m_nTextureTall;

	// find in page state
	bool m_bInFind;
	CUtlString m_sLastSearchString;

	CUtlString m_sURLToLoad; // url to load once the browser is created
	CUtlString m_sURLPostData; // post data for url to load

	CUtlString m_sCurrentURL; // the current url we have loaded
	CUtlString m_sHTMLTitle; // the title of the page we are on

	int m_iBrowser; // our browser handle

	float m_flZoom; // what zoom level we are at
	bool m_bLastKeyFocus; // tracking for when key focus changes in style application

	struct ScrollControl_t
	{
		ScrollControl_t()
		{
			Reset();
		}

		void Reset()
		{
			m_flOffsetTextureScroll = 0.0f;
			m_bScrollingUp = false;
			m_flLastScrollTime = 0.0f;
		}

		float m_flOffsetTextureScroll; // amount the html texture is scrolled around the panel itself
		bool m_bScrollingUp; // we are scrolling up (or left) on the page last?
		double m_flLastScrollTime; // when did we scroll in this direction last, used for accel curve
	};

	ScrollControl_t m_ScrollUp;
	ScrollControl_t m_ScrollLeft;

	struct ScrollData_t
	{
		ScrollData_t() 
		{
			m_bVisible = false;
			m_nMaxScroll = m_nScroll = m_nPageSize = m_nWebScroll = 0;
		}

		bool operator==( const ScrollData_t &rhs ) const
		{
			return rhs.m_bVisible == m_bVisible &&
				rhs.m_nScroll == m_nScroll &&
				rhs.m_nPageSize == m_nPageSize &&
				rhs.m_nMaxScroll == m_nMaxScroll;
		}

		bool operator!=( const ScrollData_t &rhs ) const
		{
			return !operator==(rhs);
		}

		bool m_bVisible; // is the scroll bar visible
		int m_nMaxScroll; // most amount of pixels we can scroll
		int m_nPageSize; // the underlying size of the page itself in pixels
		int m_nScroll; // currently scrolled amount of pixels
		int m_nWebScroll; // last scrolled return value from cef, not updated locally
	};
	bool ScrollHelper( ScrollControl_t &scrollControl, float flScrollDelta, int iMaxScrollOffset, ScrollData_t &scrollBar, float &flScrollHTMLAmount, bool bApplyBezier ); // shared code when scrolling around the page
	bool SetupScrollBar( const ScrollData_t & scrollData, bool bHorizontal, float flContentSize, float flMaxSize );
	CScrollBar *MakeScrollBar( bool bHorizontal);

	ScrollData_t m_scrollHorizontal; // details of horizontal scroll bar
	ScrollData_t m_scrollVertical; // details of vertical scroll bar
	CCubicBezierCurve< Vector2D > m_ScrollBezier; // the curve to scale scroll accel by

	struct LinkAtPos_t
	{
		LinkAtPos_t() { m_bLiveLink = false; }
		CUtlString m_sURL;
		bool m_bLiveLink;
		bool m_bInput;
	};
	LinkAtPos_t m_LinkAtPos; // cache for link at pos requests, because the request is async

	// last position we saw the mouse at
	float m_flCursorX;
	float m_flCursorY;

	double m_flGamePadInputTime; // last time we saw input from the gamepad

	bool m_bPopupVisible; // true if a popup menu is visible on the client
	bool m_bCanGoBack;
	bool m_bCanGoForward;
	bool m_bIgnoreMouseBackForwardButtons;

	int m_nHTMLPageWide; // last size we told CEF the page should be 
	int m_nHTMLPageTall;

	CHTMLVerticalScrollBar *m_pVerticalScrollBar; // our own copy of the scroll bars that we control manually
	CHTMLHorizontalScrollBar *m_pHorizontalScrollBar;
	float m_flLastVeritcalScrollPos;
	float m_flLastHorizontalScrollPos;

	static uint32 sm_PaintCount;
	uint32 m_PageLoadCount; // the number of posturl calls we have made
#if !defined( SOURCE2_PANORAMA ) && !defined( PANORAMA_PUBLIC_STEAM_SDK )
	CUtlVector<HTMLCommandBuffer_t *> m_vecPendingMessages;
#endif
	bool m_bSuppressTextureLoads;

	bool m_bCaptureThumbNailThisFrame;
	CPanel2D *m_pCaptureEventTarget;
	int m_nCaptureUserData;

	bool m_bCommenceZoomOperationOnTextureUpload; // when the next texture upload is ready, should we apply scale/offset transforms we have saved above
	float m_flHorizScrollOffset; // the offset between page scroll and texture scroll we had at the start of a zoom
	float m_flVertScrollOffset; // the offset between page scroll and texture scroll we had at the start of a zoom
	bool m_bFullScreen; // are we in fullscreen right now?
	bool m_bConfigureYouTubeHTML5OptIn; // are we doing the forcefully opt into youtube html5 beta path
	bool m_bMousePanningActive; // true if the mouse is in panning mode
	Vector2D m_vecMousePanningPos; // the x,y pos of the mouse over the panel when middle panning started
	CImagePanel *m_pMousePanningImage; // the image to show when panning
	CCubicBezierCurve< Vector2D > m_MousePanBezier; // the curve to scale panning accel by
	bool m_bIsSecure; // is this page ssl secure?
	bool m_bIsCertError; // did we have a cert error when loading? 
	bool m_bIsEVCert; // is it an EV cert?
	CUtlString m_sCertName; // who was the cert issued to?
	bool m_bEmbedded; // if true we are embedded instance, just show html pages and simple scrolling, not complex interactions
	bool m_bAllowOverScroll; // if true allow scrolling the edge of the texture beyond the edge of the screen (i.e so you can hover the recticle at any point)
	bool m_bLastScrollbarSetupAllowedOverScroll;
	float m_flMouseLastX;
	float m_flMouseLastY;
	float m_flLastSteamPadScroll;
	uint32 m_unSteamPadScrollRepeats;

	panorama::HtmlFormHasFocus_t m_evtFocus;		// used for saving state of controls that have focus info dispatched

	bool m_bPendingInputZoom; // true if we are zooming into an input element
	bool m_bFocusEventSentForClick; // time we sent a focus event to the browser
	bool m_bDidMousePanWhileMouseDown; // did we do panning with the mouse held down
	bool m_bWaitingForZoomResponse;

	Vector2D m_LastSteamRightPad;

	panorama::CTextTooltip *m_pTooltip;
	bool m_bGotKeyDown;
#if defined( SOURCE2_PANORAMA ) || defined( PANORAMA_PUBLIC_STEAM_SDK )
	HHTMLBrowser m_HTMLBrowser;
	void OnBrowserReady( HTML_BrowserReady_t *pBrowserReady, bool bIOFailure );
	CCallResult< CHTML, HTML_BrowserReady_t > m_SteamCallResultBrowserReady;

	CPanelPtr< CFileOpenDialog > m_pFileOpenDialog;
	
	CUtlVector<HHTMLBrowser> m_vecDenyNewBrowserWindows;
#else
	CChromePaintBufferClient m_SharedPaintBuffer;
#endif

	int m_nPopupX;
	int m_nPopupY;
	int m_nPopupWide;
	int m_nPopupTall;
	int m_nTextureSerialCombo;
	CUtlBuffer m_ComboTexture;
	CHTML *m_pPopupChild;

	CThreadMutex m_mutexHTMLTexture;
	CThreadMutex m_mutexScreenShot;
	CUtlBuffer m_bufScreenshotTexture;
	float m_flScrollRemainder;
	int m_nTargetHorizontalScrollValue;
	int m_nTargetVerticalScrollValue;
	bool m_bControlPageScrolling;

	// virtual mouse used when steam controller is connected
	IVirtualMouse *m_pLeftMousePad;
	Vector2D m_vecVirtualScrollPrev;
	Vector2D m_vecVirtualScrollOrigin;
	bool m_bVerticalAxisSnap;
	bool m_bClickingLeftPad;
	bool m_bMarkZoomStart;
	float m_flInitialZoomLevel;
	float m_flZoomSwipeOriginPosition;

	static const float s_fScrollDeadzoneScale;
};

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CHTMLSimpleNavigationWrapper : public CPanel2D
{
	DECLARE_PANEL2D( CHTMLSimpleNavigationWrapper, CPanel2D );

public:
	CHTMLSimpleNavigationWrapper( CPanel2D *pParent, const char *pchPanelID );

	virtual bool BSetProperty( CPanoramaSymbol symName, const char *pchValue ) OVERRIDE;

	virtual bool OnGamePadDown( const GamePadData_t &code ) OVERRIDE;
	virtual bool OnGamePadAnalog( const GamePadData_t &code ) OVERRIDE;
	virtual bool OnMouseWheel( const MouseData_t &code ) OVERRIDE;

private:
	void EnsureHTMLPanelReference();

private:
	CHTML *m_pHTML;
	CPanoramaSymbol m_symWrappedHTMLID;
	bool m_bInEventProcessing;
};

} // namespace panorama

#endif // PANORAMA_HTML_H