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

#ifndef PANORAMA_TEXTENTRY_H
#define PANORAMA_TEXTENTRY_H

#ifdef _WIN32
#pragma once
#endif

#include "panel2d.h"
#include "panorama/text/iuitextlayout.h"
#include "panorama/textinput/textinput.h"
#include "panorama/textinput/textinput_settings.h"
#if !defined(SOURCE2_PANORAMA)
#include "../common/globals.h"
#include "../common/reliabletimer.h"
#include "../common/framefunction.h"
#endif
#include "panorama/uischeduleddel.h"
#include "imesystem/imeuiinterface.h"

namespace panorama
{

class CLabel;
class CTextEntryAutocomplete;

#if defined( SOURCE2_PANORAMA )
class CTextEntryIMEControls;
#endif // defined( SOURCE2_PANORAMA )

DECLARE_PANEL_EVENT1( TextEntrySubmit, const char * );		// always raised when user is done submitting text
DECLARE_PANEL_EVENT0( TextEntryChanged );					// call CTextEntry::RaiseChangeEvents() to enable
DECLARE_PANEL_EVENT0( TextEntryShowTextInputHandler );
DECLARE_PANEL_EVENT0( TextEntryHideTextInputHandler );
DECLARE_PANEL_EVENT0( TextEntryInsertFromClipboard );
DECLARE_PANEL_EVENT0( TextEntryCopyToClipboard );
DECLARE_PANEL_EVENT0( TextEntryCutToClipboard );

//-----------------------------------------------------------------------------
// Purpose: text entry
//-----------------------------------------------------------------------------
class CTextEntry : public CPanel2D, public ITextInputControl
#if defined( SOURCE2_PANORAMA )
	, public IIMEUITextField
#endif // defined( SOURCE2_PANORAMA )
{
	DECLARE_PANEL2D( CTextEntry, CPanel2D );

public:
	CTextEntry( CPanel2D *parent, const char * pchPanelID );
	virtual ~CTextEntry();

	virtual void SetupJavascriptObjectTemplate() OVERRIDE;

	void SetUndoHistoryEnabled( bool bEnabled );
	void ClearUndoHistory();

	virtual bool OnKeyDown( const KeyData_t &code );
	virtual bool OnKeyTyped( const KeyData_t &unichar );
	virtual bool OnKeyUp( const KeyData_t & code ) { return BaseClass::OnKeyUp( code ); }
	virtual void Paint();
	virtual bool BSetProperty( CPanoramaSymbol symName, const char *pchValue ) OVERRIDE;
	virtual void OnInitializedFromLayout();
	virtual void OnStylesChanged();

	virtual void OnMouseMove( float flMouseX, float flMouseY );
	virtual bool OnMouseButtonDown( const MouseData_t &code );
	virtual bool OnMouseButtonUp( const MouseData_t &code );
	virtual bool OnMouseButtonDoubleClick( const MouseData_t &code );
	virtual bool OnMouseButtonTripleClick( const MouseData_t &code );

	virtual void OnLayoutTraverse( float flFinalWidth, float flFinalHeight );
	virtual void GetDebugPropertyInfo( CUtlVector< DebugPropertyOutput_t *> *pvecProperties ) OVERRIDE;

	virtual bool BRequiresContentClipLayer() { return m_bMayDrawOutsideBounds; }

	void SetMode( ETextInputMode_t mode );
	ETextInputMode_t GetMode() { return m_modeInput; }

	void SetText( const char *pchValue );
	const char *PchGetText() const;
	const wchar_t *PwchGetText() const;
	void SetPlaceholderText( const char *pchValue );
	const char *PchGetPlaceholderText() const;
	void SetMaxChars( uint unMaxChars );
	uint GetCharCount() const { return m_vecWCharData.Count() - 1; }	// m_vecWCharData is terminated, so remove extra char count
	uint GetMaxCharCount() const { return m_unMaxChars; }
	int32 GetCursorOffset() const { return m_nCursorOffset; }
	void SetCursorOffset( int32 nCursoroffset );
	bool BSupportsImmediateTextReturn() { return true; }
	void RequestControlString() { Assert( false ); } // no op, we already have the string

	// Insert clipboard contents into text entry
	void InsertFromClipboard();

	// Cut selected text to clipboard
	void CutToClipboard();

	// Copy selected text to clipboard
	void CopyToClipboard();

	bool OnInsertFromClipboard( const CPanelPtr< IUIPanel > &pPanel );
	bool OnCutToClipboard( const CPanelPtr< IUIPanel > &pPanel );
	bool OnCopyToClipboard( const CPanelPtr< IUIPanel > &pPanel );

	void SetAlwaysRenderCaret( bool bAlwaysRenderCaret );

	// Delete currently selected text 
	void DeleteSelection( bool bDontPushUndoHistory );

	// Clear selection region, leaving nothing selected
	void ClearSelection();

	// select all the text in the control
	void SelectAll();

	// Lock the selection in place; it can only be unlocked or deleted
	void LockSelection( bool bLockSelection ) { m_bSelectionLocked = bLockSelection; }

	// Insert characters at cursor
	void InsertCharacterAtCursor( const wchar_t &unichar );
	void InsertCharactersAtCursor( const wchar_t *pwch, size_t cwch );

	bool BIsValidCharacter( const wchar_t wch );

	void RaiseChangeEvents( bool bEnable ) { m_bRaiseChangeEvents = bEnable; }

	virtual EMouseCursors GetMouseCursor();

	// ITextInputControl helpers
	virtual CPanel2D *GetAssociatedPanel() { return this; }

	panorama::CTextInputHandler *GetTextInputHandler() { return m_pTextInputHandler.Get(); }
	void SetTextInputHandler( panorama::CTextInputHandler *pTextInputHandler );

	virtual bool BRequiresFocus() OVERRIDE { return true; }

	// Autocomplete
	void ClearAutocomplete( void );
	void AddAutocomplete( const char *pszOption );

#ifdef DBGFLAG_VALIDATE
	virtual void ValidateClientPanel( CValidator &validator, const tchar *pchName );
#endif
	int32 GetSelectionStart() { return m_nSelectionStartIndex; }
	int32 GetSelectionEnd() { return m_nSelectionEndIndex; }

	// Interface for IIMEUITextField
#if defined( SOURCE2_PANORAMA )
	virtual void IME_SetLoggingChannel( LoggingChannelID_t loggingChannel ) OVERRIDE;
	virtual bool IME_IsEnabled() OVERRIDE;
	virtual IMEUIObjectType IME_GetObjectType() OVERRIDE;
	virtual wchar_t *IME_GetCompositionString() OVERRIDE;
	virtual void IME_CreateCompositionString() OVERRIDE;
	virtual void IME_ClearCompositionString() OVERRIDE;
	virtual void IME_CommitCompositionString( const wchar_t *pString ) OVERRIDE;
	virtual void IME_SetCompositionStringText( const wchar_t *pString ) OVERRIDE;
	virtual void IME_SetCompositionStringPosition( uint32 nPos ) OVERRIDE;
	virtual void IME_SetCursorInCompositionString( uint32 nPos ) OVERRIDE;
	virtual uint32 IME_GetCaretIndex() OVERRIDE;
	virtual uint32 IME_GetBeginIndex() OVERRIDE;
	virtual uint32 IME_GetEndIndex() OVERRIDE;
	virtual void IME_ReplaceCharacters( const wchar_t *pString, uint32 nStart, uint32 nEnd ) OVERRIDE;
	virtual void IME_SetSelection( uint32 nStart, uint32 nEnd ) OVERRIDE;
	virtual void IME_SetWideCursor( bool bWide ) OVERRIDE;
	virtual void IME_HighlightCompositionStringText( uint32 nPos, uint32 nLen, IMETextHighlightStyle highlightStyle ) OVERRIDE;
	virtual void IME_DeleteSelection() OVERRIDE;
	virtual	void IME_RemoveInputWindow() OVERRIDE;
	virtual	void IME_DisplayInputWindow( const wchar_t *pReadingString, const IMERectF *pPosition ) OVERRIDE;
	virtual	void IME_RepositionInputWindow( const IMERectF *pPosition ) OVERRIDE;
	virtual	void IME_CreateList( int nPageSize, int nListStartsAt1 ) OVERRIDE;
	virtual	void IME_RemoveList() OVERRIDE;
	virtual	void IME_ClearList() OVERRIDE;
	virtual	void IME_ShowList( bool bShow ) OVERRIDE;
	virtual	void IME_RepositionCandidateList( const IMERectF *pPosition ) OVERRIDE;
	virtual	void IME_SelectItemInList( int32 nItemToSelect ) OVERRIDE;
	virtual	void IME_AddToList( const wchar_t *pCandidateString ) OVERRIDE;
#endif // defined( SOURCE2_PANORAMA )

protected:
	virtual void OnContentSizeTraverse( float *pflContentWidth, float *pflContentHeight, float flMaxWidth, float flMaxHeight, bool bFinalDimensions );
	virtual bool BIsClientPanelEvent( CPanoramaSymbol symProperty ) OVERRIDE;

private:
	bool OnTextEntryShowTextInputHandler( const panorama::CPanelPtr< panorama::IUIPanel > &ptrPanel );
	bool OnTextEntryHideTextInputHandler( const panorama::CPanelPtr< panorama::IUIPanel > &ptrPanel );
	bool EventActivated ( const CPanelPtr< IUIPanel > &pPanel, EPanelEventSource_t eSource );
	bool OnTextEntryScrollToCursor( const panorama::CPanelPtr< panorama::IUIPanel > &ptrPanel );
	bool EventInputFocusTopLevelChanged( CPanelPtr< CPanel2D > ptrPanel );
	bool HandleTextInputFinished( const panorama::CPanelPtr< panorama::IUIPanel > &pPanel, bool bFinished, char const *pchText );
	bool EventInputFocusSet( const CPanelPtr< IUIPanel > &ptrPanel );
	bool EventInputFocusLost( const CPanelPtr< IUIPanel > &ptrPanel );

	IUITextLayout *CreateTextLayout( float flWidth, float flHeight );
	void RemoveCharacter( int32 offset );
	void RaiseTextChangedEvent();

	void UpdateSelectionToInclude( int32 unPreviousCursor, int32 unNewCursorPos );

	void Undo();
	void Redo();
	void PushUndoStack();
	void PushRedoStack();

	void UpdateCapsLockWarning();
	void OnScheduledCapsLockCheck();

	void MoveCaretToEnd( bool bIsShiftHeld );

	const wchar_t *PwchGetTextDisplay(); // honors password-entry mode

	bool m_bUndoHistoryEnabled;

	bool m_bContentSizeDirty;				// content size is dirty - text has changed

	float m_flMaxWidthLastContentSize;
	float m_flMaxHeightLastContentSize;

	bool m_bCaretPositionDirty;
	bool m_bAlwaysRenderCaret;

	bool m_bMayDrawOutsideBounds;

	bool m_bShowTextInputHandlerOnLeftMouseUp;

	bool m_bSelectionLocked;
	bool m_bMultiline;					// used to determine whether to swallow multiline-only characters (\n, etc.)

	Vector2D m_LastMousePos;
	CUtlVector<wchar_t> m_vecWCharData;
	CUtlVector<wchar_t> m_vecWCharDataPasswordDisplay;
	mutable CUtlString m_UTF8String;
	mutable bool m_bUTF8StringInvalid;
	uint32 m_unMaxChars;

	int32 m_nCursorOffset;
	Vector2D m_CaretCoords;
	float m_flCaretHeight;

	bool m_bLeftMouseIsDown;
	bool m_bSelectionRectDirty;
	int32 m_nSelectionStartIndex;
	int32 m_nSelectionEndIndex;

	bool m_bScrollableSizeDirty;
	float m_flLastFinalWidthToScrollable;
	float m_flLastFinalHeightToScrollable;

	// Translation of text in single line entries to scroll to show where the cursor is
	float m_flTextXTranslate;
   
	CPanelPtr< CTextInputHandler > m_pTextInputHandler; 

	CUtlVector<IUITextLayout::HitTestRegionRect_t> m_vecSelectionRects;
	panorama::CTextInputHandlerSettings m_settingsTextInput;

	CUtlVector< wchar_t * > m_vecUndoStack;
	CUtlVector< wchar_t * > m_vecRedoStack;

	double m_flFocusTime;

	// only raise text changed events when requested, as we have to convert every character to UTF8 from unicode
	bool m_bRaiseChangeEvents;

	ETextInputMode_t m_modeInput;

	bool m_bDisplayInput;
	bool m_bWarnOnCapsLock;
	panorama::CUIScheduledDel m_scheduledCapsLockCheck;

	CLabel *m_pPlaceholderText;
	
	CPanelPtr< CTextEntryAutocomplete > m_pAutocompleteMenu;

#if defined( SOURCE2_PANORAMA )
	// IME State
	bool m_bIMEWideCursor;
	CUtlVector< wchar_t > m_IMECompositionString;
	int32 m_nIMECompositionCursor;
	int32 m_nIMEStartingCursorInsertionOffset;
	int32 m_nIMEEndingCursorInsertionOffset;
	bool m_bIMERejectBackspace;

	CPanelPtr< CTextEntryIMEControls > m_pIMEControls;
	LoggingChannelID_t m_IMELoggingChannel;
#endif // defined( SOURCE2_PANORAMA )
};

class CTextEntryAutocomplete : public CPanel2D
{
	DECLARE_PANEL2D( CTextEntryAutocomplete, CPanel2D );

public:
	CTextEntryAutocomplete( CTextEntry *pParent, const char *pchPanelID );
	virtual ~CTextEntryAutocomplete();

	void DeleteSelf( bool bSetFocusToTarget = true );

	void AddOption( const char *pszOption );

private:
	// forward keys, arrows back to my parent
	virtual bool OnKeyDown( const KeyData_t &code );
	virtual bool OnKeyUp( const KeyData_t & code );
	virtual bool OnKeyTyped( const KeyData_t &unichar );

	void PositionNearParent();

	virtual void OnLayoutTraverse( float flFinalWidth, float flFinalHeight );

	// events
	bool EventPanelActivated( const CPanelPtr< IUIPanel > &pPanel, EPanelEventSource_t eSource );
	bool EventInputFocusSet( const CPanelPtr< IUIPanel > &pPanel );

	void SuggestionSelected( CLabel *pLabel );

	CPanelPtr< CTextEntry > m_pTextEntryParent;
	bool m_bClosing;
};

#if defined( SOURCE2_PANORAMA )
class CTextEntryIMEControls : public CPanel2D
{
	DECLARE_PANEL2D( CTextEntryIMEControls, CPanel2D );

public:
	CTextEntryIMEControls( CTextEntry *pParent, const char *pchPanelID );
	virtual ~CTextEntryIMEControls();

	void ClearCandidateList();
	void CreateCandidateList( int nPageSize, int nListStartsAt1 );
	void AddCandidate( const wchar_t *pszCandidateString );
	void SetSelectedCandidate( int nItemToSelect );
	void ShowCandidateList( bool bShow );

	void SetReadingString( const wchar_t *pReadingString );

private:
	void PositionNearParent();

	virtual void OnLayoutTraverse( float flFinalWidth, float flFinalHeight );

	CPanelPtr< CTextEntry > m_pTextEntryParent;

	CPanelPtr< CLabel > m_pReadingStringLabel;
	CPanelPtr< CPanel2D > m_pCandidateList;

	int m_nCandidateListPageSize;
	int m_nCandidateListSelectedIndex;

	bool m_bShowCandidateList;
};
#endif // defined( SOURCE2_PANORAMA )

} // namespace panorama

#endif // PANORAMA_TEXTENTRY_H