/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     03/04/1999
// RCS-ID:      $Id: tdefs.h,v 1.1 2001/04/22 20:02:11 aleksandras Exp $
// Copyright:   (c) Aleskandars Gluchovas
// Licence:   	GPL (Version 2.0 or later)
/////////////////////////////////////////////////////////////////////////////

// NOTE:: tab-size for this source is 4


#ifndef __LSEDITOR_G__
#define __LSEDITOR_G__

#include "wx/dynarray.h"
#include "wx/list.h"

#include "wx/window.h"
#include "wx/scrolbar.h"

#include "wx/pmf/sourcepainter.h"

#define WXTEXTEDITOR_VERSION_MAJOR 0
#define WXTEXTEDITOR_VERSION_MINOR 3

#define NPOS ((size_t)(-1))

class wxTextEditorModel;
class wxTextEditorView;

/*
 * class represents column-row position in the source text,
 * may refere to the column past the end-of-line,
 * but should not point past the last-line in the text
 */

class TPosition
{
public:
	inline TPosition() : mRow(0), mCol(0) {}

	inline TPosition( size_t row, size_t col )

		: mRow( row ), mCol( col ) {}

	inline bool operator>( const TPosition& rhs ) 
	{ 
		if ( mRow == rhs.mRow ) return mCol > rhs.mCol;
		else
			return mRow > rhs.mRow;
	}

public:
	size_t mRow;
	size_t mCol;
};

class TRange
{
public:
	TPosition mFrom;
	TPosition mTill;

	TRange() {}
	TRange( const TPosition& from, const TPosition& till )
		: mFrom( from ), mTill( till )
	{}
};

inline bool operator==( const TPosition& lhs, const TPosition& rhs )
{
	return lhs.mRow == rhs.mRow && lhs.mCol == rhs.mCol;
}

// OLD STUFF::
//#define MAX_BLOCK_LEN      (1024*164)
//#define BALANCED_BLOCK_LEN (1024*152)		 
//#define FILLED_BLOCK_LEN   (1024*148)

#define MAX_BLOCK_LEN      (4096)
#define FILLED_BLOCK_LEN   (3500)
#define BALANCED_BLOCK_LEN (3000)		 

// FOR NOW::
#define T_ASSERT( x ) if ( !(x) ) throw;

// to speed up debug v. :

#define is_eol_char( ch )     ( ch == (char)10 )
#define is_DOS_eol_char( ch ) ( ch == (char)13 )

// the target-platfrom eol-marking is selected when
// new text document is created or auto-detection
// failed to determine the text-format (e.g. no EOLs found at all)

#if defined(__WINDOWS__) || defined(__WXMSW__)

	  #define IS_UNIX_TEXT_BY_DEFAULT TRUE
#else
	  #define IS_UNIX_TEXT_BY_DEFAULT FALSE
#endif

//inline bool is_eol_char( char ch ) { return ch == 10 && ch == 13 ; }

/*
 * Class contains single fragment of the source text, which
 * may grow or shrink in the process of editing. Blocks always
 * start at the begining of the line and end at eol, i.e. lines
 * are not broken among blocks
 */

class TBlock
{
public:
	char mBuf[MAX_BLOCK_LEN];
	size_t mTextLen;
	size_t mRowCount;

	TBlock() : mTextLen(0), mRowCount(0) { mBuf[0] = '\0'; }

	void RecalcBlockProperties();

	bool operator==( const TBlock& blk ) const { return this == &blk; }

	bool operator<( const TBlock& blk ) const { return TRUE; }
};

/*
 * captures info about mutable command 
 */

class TCommand
{
public:

	TCommand() : mType(-1) {}
	TCommand( int type ) : mType( type ) {}
	~TCommand() {}

	int       mType;
	char*     mData;
	size_t    mDataLen;

	TRange    mRange;

	// positions of cursor before and after executions of this command
	TPosition mPrePos;
	TPosition mPostPos;

};

enum TEXT_EDITOR_COMMAND
{
	TCMD_INSERT,
	TCMD_DELETE
};

enum TEXT_CHANGE_TYPE
{
	CT_MODIFIED,
	CT_DELETED,
	CT_INSERTED
};

class wxTextEditorView;

// STL-list is used for managing blocks, since it's alg. collects
// removed elements into a free-list, from which they
// can be reclaimed later, that way heap-fragmentation may be reduced

WX_DEFINE_ARRAY( TBlock*, TBlockListT );

WX_DEFINE_ARRAY( TCommand*, TCommandListT );

typedef TBlock** TBlockIteratorT;

WX_DEFINE_ARRAY( wxTextEditorView*, TextViewListT );

/*
 * class shields the higher-level operations from direct access
 * to blocks of fragmented in-memory buffers
 */

class TTextIterator
{
public:
	TBlockIteratorT   mBlockIter;
	TBlockIteratorT   mEndOfListIter;
	TPosition         mPos;

	size_t            mActualRow;
	size_t            mFirstRowInBlock;

	char*             mpCurRowStart;
	bool              mIsEof;

public:
	TTextIterator();

	char GetChar();
	bool IsEol();
	bool IsEof();
	bool IsLastLine();
	int  GetDistFromEol();

	void NextChar();
	void PreviousChar();
	void NextWord();
	void PreviousWord();
	void ToEndOfLine();
	void ToStartOfLine();

	bool IsInLastBlock();

	// accesors

	size_t    GetLineLen();
	TPosition GetPosition();

	char* GetClosestPos(); 

	// "inclusive" does not exclude dos 0x0D characters when
	// positioned at eol. It can be used while copying blocks of text (e.g. to clipboard)

	char* GetClosestPosInclusive(); 

	char* GotoClosestPos();

	inline char* GetBlockStart() { return (*mBlockIter)->mBuf; }
	inline char* GetBlockEnd()   { return (*mBlockIter)->mBuf + (*mBlockIter)->mTextLen; }

	bool MoveToNextBlock();

	bool DetectUnixText();

	// adjust this member to add specific separators, 
	// the default value is : ",.()[]\t\\+-*/|=<>:;\t\n~?!%"

	static wxString mSeparators;

	static bool IsSeparator( char ch );
};

class wxTextEditorModel;

class TTextChangeListenerBase
{
public:
	virtual void OnTextChanged( wxTextEditorModel* pModel, size_t atRow, size_t nRows, TEXT_CHANGE_TYPE ct ) = 0;
};

class TCppJavaHighlightListener : public TTextChangeListenerBase
{
protected:
	wxTextEditorModel* mpModel; // is set up temporarely

	enum { IN_COMMENT_STATE, OUT_OF_COMMENT_STATE };

public:
	virtual void OnTextChanged( wxTextEditorModel* pModel, size_t atRow, size_t nRows, TEXT_CHANGE_TYPE ct );
};


/*
 * Base class for user-defined "bookmarks" within the source-text, bookmarks
 * are automatically repositioned or deleted as the text is edited. Class
 * can be subclassed to add pin-specific data (e.g. breakpoint information)
 */

class TPinBase
{
public:
	int    mTypeCode;
	size_t mRow;

public:
	TPinBase() 
		: mTypeCode(-1), mRow(NPOS) {}

	TPinBase( int typeCode, size_t row )
		: mTypeCode( typeCode ), mRow( row ) {}

	size_t GetRow()      { return mRow; }
	int    GetTypeCode() { return mTypeCode; }

	virtual ~TPinBase() {}
};

// "recommened" type-code ranges for custom pins

#define HIHGLIGHTING_PINS_TC_STARRT 50
#define OTHER_PINS_TC_START         100

inline bool operator<( const TPinBase& lhs, TPinBase& rhs )

	{ return lhs.mRow < rhs.mRow; }

typedef TPinBase* TPinBasePtrT;

WX_DEFINE_ARRAY( TPinBase*, PinListT );

WX_DEFINE_ARRAY( TTextChangeListenerBase*, ChangeListenerListT );

/* OLD STUFF::

struct TPinBaseCompareFunctor
{
	inline int operator()(const TPinBasePtrT* x, const TPinBasePtrT*& y ) const 
	{ 
		return x->mLine < y->mLine;
	}
};

typedef WXSTL_MULTIMAP( TPinBasePtrT, TPinBasePtrT, TPinBaseCompareFunctor ) PinMapT;
typedef PinMapT::iterator PinIteratorT;
*/

/* 
 * Class manages access and manpulation of in-memory text. Can
 * be accessed by multiple views, only one of which can be active
 * at a time.
 */

class wxTextEditorModel : public wxObject
{
public:
	
	wxTextEditorModel();
	virtual ~wxTextEditorModel();

	// utilities

	char* AllocCharacters( size_t n );
	char* AllocCharacters( size_t n, const char* srcBuf );
	void FreeCharacters( char* buf );

	void DeleteSelection();
	TTextIterator CreateIterator( const TPosition& pos );

	void DeleteRange( const TPosition& from, const TPosition& till );
	void InsertText( const TPosition& pos, const char* text, size_t len );
	void InsertText( const TPosition& pos, const wxString& text );
	void AppendText( const wxString& text );

	void GetTextFromRange( const TPosition& from, const TPosition& till, char** text, size_t& textLen );
	void LoadTextFromFile( const wxString& fname );
	void SaveTextToFile( const wxString& fname );
	void ResetSelection();
	void ClearUndoBuffer();


	void DeleteAllText();
	void GetAllText( char** text, size_t& textLen );

	void SetSelectionEditMode( bool editIsOn );

	/*** user-level commands ***/

	// mutable (undoable) commands

	void OnInsertChar( char ch );
	void OnDelete();
	void OnDeleteBack();
	void OnDeleteLine();

	void OnShiftSelectionIndent( bool left );

	// clipboard functions

	void OnCopy();
	void OnPaste();
	void OnCut();
	bool CanCopy();
	bool CanPaste();

	// undo-redo

	bool CanUndo();
	bool CanRedo();
	void OnUndo();
	void OnRedo();

	// imutable commands

	void OnMoveLeft();
	void OnMoveRight();
	void OnMoveUp();
	void OnMoveDown();

	void OnWordLeft();
	void OnWordRight();

	void OnMoveToPosition( const TPosition& pos );

	void OnEndOfLine();
	void OnStartOfLine();
	void OnPageUp();
	void OnPageDown();
	void OnSlideUp();
	void OnSlideDown();
	void OnStartOfText();
	void OnEndOfText();

	void OnSelectWord();
	void OnSelectAll();

	// bookmarks

	void OnToggleBookmark();
	void OnNextBookmark();
	void OnPreviousBookmark();

	// search

	bool OnFind();
	bool OnFindNext( bool quiet = FALSE );
	bool OnFindPrevious( bool quiet = FALSE );
	void OnGotoLine( int line, int col );
	void OnGotoLine();

	// status

	bool IsReadOnly();
	bool IsModified();
	bool IsInsertMode();

	// check-pointin

	void SetCheckpoint();
	bool CheckpointModified();

	// accessors

	TPosition GetStartOfSelection();
	TPosition GetEndOfSelection();
	TPosition GetCursor();

	size_t    GetTotalRowCount();
	bool	  SelectionIsEmpty();
	bool      IsLastLine( const TPosition& pos );

	bool      IsUnixText() { return mIsUnixText; }

	void GetSelection( char** text, size_t& textLen );

	void SetStartOfSelection( const TPosition& pos );
	void SetEndOfSelection( const TPosition& pos );
	void SetCursor( const TPosition& pos );

	void NotifyTextChanged( size_t atRow, size_t nRows, TEXT_CHANGE_TYPE ct );
	void NotifyTextChanged( TPosition from, TPosition till, TEXT_CHANGE_TYPE ct );

	// does on-screen updates
	void NotifyView();
	void NotifyAllViews();

	void AddView( wxTextEditorView* pView );
	void RemoveView( wxTextEditorView* pView );
	void SetActiveView( wxTextEditorView* pView );
	wxTextEditorView* GetActiveView();

	void SetRowsPerPage( size_t n );

	void AddPin( TPinBase* pPin );
	PinListT& GetPins(); 

	// returns NPOS, if non
	size_t FindFirstPinInRange( size_t fromRow, size_t tillRow );
	size_t FindNextPinFrom( size_t fromRow );
	size_t FindPreviousPinFrom( size_t fromRow );

	size_t GetPinNoAt( size_t row, int pinTypeCode );
	TPinBase* GetPinAt( size_t row, int pinTypeCode );
	void RemovePinAt( size_t row, int pinTypeCode );

	void AddChangeListener( TTextChangeListenerBase* pListener );

protected:
	size_t GetLineCountInRange( char* from, char* till ); 
	size_t GetLineAlignedBlockSize( char* start, char* end, size_t preferredSize );
	void RecalcBlockProperties( TBlockIteratorT& iter );

	// two lowest-level operations
	void DoInsertText ( const TPosition& pos, char* text, size_t len, TRange& actualRange );
	void DoDeleteRange( const TPosition& from, const TPosition& till, TRange& actualRange );

	void DoExecuteNewCommand( TCommand& cmd );

	void DoReexecuteCommand( TCommand& cmd );
	void DoUnexecuteCommand( TCommand& cmd );


	void ExecuteCommand( TCommand* pCmd );

	// to methods enabling grouping of undo-able commands
	bool CanPrependCommand( TCommand* pCmd );
	void PrependCommand( TCommand* pCmd );

	void SetPostPos( const TPosition& pos );

	void UndoImpl();
	void RedoImpl();

	void StartBatch();
	void FinishBatch();

	void CheckSelection();
	void TrackSelection();

	void ArrangePositions( TPosition& upper, TPosition& lower );
	void ArrangePositions( size_t& upper, size_t& lower );

	void MergeChange( size_t fromRow, size_t nRows );

	void PrepreForCommand();

	size_t TextToScrColumn( const TPosition& pos );
	size_t ScrToTextColumn( TPosition pos );

	void DoMoveCursor( int rows, int cols );

protected:
	TBlockListT         mBlocks;
	
	TCommandListT       mCommands;
	size_t              mCurCommand;

	TextViewListT       mViews;
	wxTextEditorView*   mpActiveView;

	PinListT            mPins;
	bool                mIsUnixText;

	ChangeListenerListT mChangeListeners;

public:
	/*** public properties ***/

	bool          mTextChanged;
	size_t        mChangedFromRow;
	size_t        mChangedTillRow;

	bool          mWasChanged;			  // TRUE, if any content has been changed

	TPosition     mCursorPos;

	TPosition	  mPrevSelectionStart;
	TPosition     mPrevSelectionEnd;
	TPosition     mPrevCursorPos;

	TPosition     mSelectionStart;
	TPosition     mSelectionEnd;
	size_t        mRowsPerPage;

	bool          mIsReadOnly;           // default: FALSE
	bool          mIsModified;
	bool          mInsertMode;			 // default: TRUE
	bool          mAutoIndentMode;       // default: TRUE
	bool          mSmartIndentMode;      // default: TRUE

	bool          mIsSelectionEditMode;  // default: TRUE
	size_t        mTabSize;              // default: 4

	wxStringList  mSearchExprList;
	wxString      mLastFindExpr;

	bool          mCheckPointDestroyed;
	size_t        mCheckPointCmdNo;
};

class TCursorTimer;
class wxTextEditorView;

class TPinPainterBase : public wxObject
{
public:
	int    mPinTypeCode;

public:
	TPinPainterBase( int pinTc ) : mPinTypeCode( pinTc ) {}
	TPinPainterBase() : mPinTypeCode( -1 ) {}

	inline int GetPinTypeCode() { return mPinTypeCode; }

	virtual void DrawPin( TPinBase* pPin, wxTextEditorView& view, wxDC& dc, 
						  const wxPoint& pos, const wxSize& dim ) = 0;
};

/*
 * a couple very common ping objects/painters
 */

#define BOOKMARK_PIN_TC  (OTHER_PINS_TC_START)
#define BRKPOINT_PIN_TC  (BOOKMARK_PIN_TC + 1)

class TBookmarkPainter : public TPinPainterBase
{
protected:
	wxBrush mBkBrush;

public:
	TBookmarkPainter();

	virtual void DrawPin( TPinBase* pPin, wxTextEditorView& view, wxDC& dc, 
						  const wxPoint& pos, const wxSize& dim );
};

class TBookmarkPin : public TPinBase
{
public:
	TBookmarkPin( size_t row )
		: TPinBase( BOOKMARK_PIN_TC, row )
	{}

	static int GetPinTypeCode() { return BOOKMARK_PIN_TC; }
};

class TBreakpointPainter : public TPinPainterBase
{
protected:
	wxBrush mBkBrush;

public:
	TBreakpointPainter();

	virtual void DrawPin( TPinBase* pPin, wxTextEditorView& view, wxDC& dc, 
						  const wxPoint& pos, const wxSize& dim );
};

class TBreakpointPin : public TPinBase
{
public:
	TBreakpointPin( size_t row )
		: TPinBase( BRKPOINT_PIN_TC, row )
	{}

	static int GetPinTypeCode() { return BRKPOINT_PIN_TC; }
};


WX_DEFINE_ARRAY( TPinPainterBase*, PinPainterListT );

/*
 * Class displays graphical view of data contained in wxTextModel
 */

class wxTextEditorView : public wxScrolledWindow
{
protected:

	wxTextEditorModel*  mpModel;
	TPosition           mSelectionStart;
	TPosition           mSelectionEnd;
	TPosition           mCursorPos;

	TPosition           mLastViewStart;
	size_t              mLastRowsTotal;

	size_t              mRowsPerPage;
	size_t              mColsPerPage;

	static char*        mpLineBuffer;
	static size_t       mpLineBufferLen;

	bool                mFullRefreshPending;
	bool                mAdjustScrollPending;

	wxFont              mFont;

	bool                mScrollingOn; // default: TRUE
	bool                mCursorOn;    // default: TRUE;

	bool                mLTMode;      // line-tracking mode
								      // (when the whole line is coloured,
	                                  //  instead of showing blinking cursor position)

	wxColour            mLTColour;    // fill-colour for LT-mode

	bool                mDragStarted;
	char*               mpDraggedText;

	bool                mOwnsModel;

	wxString            mFragment;    // reused heap-buffer 
								      // for coloured fragments
	SourcePainter*      mpPainter;
	PinPainterListT     mPinPainters;
	TTextIterator       mCashedIter;

	static TCursorTimer* mpTimer;

public: /*** public properties ***/

	int                mLeftMargin;		// default: 20
	int                mRightMargin;	// default: 0
	int                mTopMargin;		// default: 0
	int                mBottomMargin;	// default: 0
	int                mMaxColumns;     // default: 500
	bool               mHasBgImage;
	wxBitmap           mBgImage;
	wxDC*              mpBgImageDc;

	TPosition          mPagePos;

	// color-scheme properties

	wxColour           mNormalTextCol;
	wxColour           mIndentifierTextCol;
	wxColour           mReservedWordTextCol;
	wxColour           mCommentTextCol;

	wxColour           mNormalBkCol;
	wxColour           mSelectionFgCol;
	wxColour           mSelectionBkCol;

	wxBrush            mNormalBkBrush;
	wxBrush            mSelectedBkBrush;

	// accessed by timer

	TPosition           mCursorScrPos;
	wxSize              mCharDim;

protected:

	char* GetLineBuffer( size_t len );

	virtual void PaintDecorations( size_t fromRow, size_t tillRow, 
								   wxDC& dc, TTextIterator& iter );
	virtual void PaintRows( size_t fromRow, size_t tillRow, wxDC& dc );
	virtual void PaintBackgroundRegion( wxDC& dc, int x, int y, 
										int width, int height);

	void ObtainFontProperties();

	bool IsActiveView();
	void SetTextDefaults();
	void RecalcPagingInfo();

	TPinPainterBase* FindPainterForPin( TPinBase& pin );

public:
	wxTextEditorView( wxWindow* parent, wxWindowID id = -1, 
					  wxTextEditorModel* pModel = NULL, 
					  int wndStyle = wxSUNKEN_BORDER,
					  bool ownsModel = TRUE );
	~wxTextEditorView();

	/*** setup methods ***/

	void SetModel( wxTextEditorModel* pModel );

	// sets custom syntax-higlighting implementation
	void SetSourcePainter( SourcePainter* pPainter );
	void AddPinPainter( TPinPainterBase* pPainter );

	void LoadBgImage( const wxString& fname, long type = wxBITMAP_TYPE_BMP );
	void SetBgImage( const wxBitmap& image );
	void SetDefaultFont( const wxFont& font );
	wxFont& GetDefaultFont(); 

	wxSize GetCharacterSize() { return mCharDim; }

	size_t GetRowsPerPage() { return mRowsPerPage; }
	void SetRowsPerPage( size_t n );
	void SetMaxColumns( size_t n );

	void SetLineTrackingMode( bool on, const wxColour& col = wxColour(255,255,0) );

	void EnableCursor( bool enable );
	void EnableScrollbars( bool enable );

	void SetColours( const wxColour& normalBkCol,
					 const wxColour& selectedBkCol,
					 const wxColour& selectedTextCol );

	void SetHeighlightingColours( const wxColour& normalTextCol,
								  const wxColour& identifierTextCol,
								  const wxColour& reservedWordTextCol,
								  const wxColour& commentTextCol );

	void SetMargins( int top, int left, int bottom, int right );

	// notifications from editor-model:

	void OnModelChanged();
	void ScrollView( int rows, int cols );

	// accessors

	void Activate();
	void Deactivate();

	// event handlers

	void OnScroll( wxScrollWinEvent& event );
	void OnPaint ( wxPaintEvent& event );
	void OnSize  ( wxSizeEvent& event );
	void OnEraseBackground( wxEraseEvent& event );

	void OnLButtonDown( wxMouseEvent& event );
	void OnLButtonUp  ( wxMouseEvent& event );
	void OnMotion     ( wxMouseEvent& event );
	void OnDblClick   ( wxMouseEvent& event );

	void OnSetFocus( wxFocusEvent& event );
	void OnKillFocus( wxFocusEvent& event );

	// requests editor to keep cursor blinking, even when
	// the window has lost it's focus

	void HoldCursor( bool hold );

	// FOR NOW:: hard-coded key-bindings

	void OnChar( wxKeyEvent& event );
	void OnKeyDown( wxKeyEvent& event );

	// utilities

	virtual void SyncViewPortPosition();
	virtual void SyncScrollbars();
	virtual void PositionCursor();

	void TextPosToScreenPos( const TPosition& txtPos, TPosition& scrPos );
	void ScreenPosToTextPos( const TPosition& scrPos, TPosition& txtPos );
	void ScreenPosToPixels ( const TPosition& scrPos, int& x, int& y );
	void PixelsToScrPos    ( int x, int y, int& scrRow, int& scrCol );
	void PixelsToTextPos   ( int x, int y, TPosition& textPos );

	bool IsClipboardCmd( wxKeyEvent& key );

	TPosition GetPagePos() { return mPagePos; }

	DECLARE_EVENT_TABLE()
};

// TODO:: mutex class should be used to avoid race on updates

class TCursorTimer : public wxTimer
{
protected:
	wxTextEditorView* mpView;
	volatile bool mIsLocked;
	volatile bool mIsShown;
	volatile bool mStarted;
	wxBrush       mBrush;
	bool          mMissOneTick;    // HACK:: to avoid restarting
								   //        timer under wxGTK

	int           mBlinkInterval;  // default: 500mills
protected:

	void DrawCursor();

public:

	TCursorTimer();

	virtual void Notify();

	void SetView( wxTextEditorView* pView );
	wxTextEditorView* GetView();
	void HideCursor( bool forceHide = FALSE );
	void ShowCursor( bool forceShow = FALSE );

	void SetIsShown( bool isShown );
	void Lock();
	void Unlock();
};

#endif // __LSEDITOR_G__
