#ifndef IDEEDITOR_H
#define IDEEDITOR_H

//#include <ide/Common/Common.h>
#include <CtrlLib/CtrlLib.h>
#include <plugin/pcre/Pcre.h>

namespace Upp {

#define  LAYOUTFILE <CodeEditor/CodeEditor.lay>
#include <CtrlCore/lay.h>

#define IMAGEVECTOR Vector
#define IMAGECLASS  CodeEditorImg
#define IMAGEFILE   <CodeEditor/CodeEditor.iml>
#include <Draw/iml_header.h>


void FindWildcardMenu(Callback1<const char *> cb, Point p, bool tablf, Ctrl *owner, bool regexp);

struct LineInfoRecord {
	int    lineno;
	String breakpoint;
	int    count;
	int    firstedited;
	int    edited;

	LineInfoRecord() { edited = 0; }
};

typedef Array<LineInfoRecord> LineInfo;

struct LineInfoRemRecord : Moveable<LineInfoRemRecord> {
	int    firstedited;
	int    edited;
};

typedef Vector<LineInfoRemRecord> LineInfoRem;

void Renumber(LineInfo& lf);
void ClearBreakpoints(LineInfo& lf);
void ValidateBreakpoints(LineInfo& lf);

class CodeEditor;

class EditorBar : public FrameLeft<Ctrl> {
public:
	virtual void Paint(Draw& w);
	virtual void MouseMove(Point p, dword flags);
	virtual void MouseLeave();
	virtual void LeftDown(Point p, dword flags);
	virtual void LeftDouble(Point p, dword flags);
	virtual void RightDown(Point p, dword flags);
	virtual void MouseWheel(Point p, int zdelta, dword keyflags);

private:
	struct LnInfo : Moveable<LnInfo> {
		int    lineno = -1;
		String breakpoint;
		int    firstedited = 0;
		int    edited = 0;
		Image  icon;
		String annotation;
	};
	
	Vector<LnInfo>   li;
	LineInfoRem      li_removed;

	CodeEditor       *editor;
	int              ptrline[2];
	Image            ptrimg[2];
	bool             bingenabled;
	bool             hilite_if_endif;
	bool             line_numbers;
	bool             no_annotations;
	int              annotations;
	bool             ignored_next_edit;
	int              next_age;
	int              active_annotation;
	Vector<Color>    animate;
	Image            status_image;
	Color            bg_color = Null;
	String           text;
	Color            text_color = Null;

	String& PointBreak(int& y);
	void    sPaintImage(Draw& w, int y, int fy, const Image& img);
	
	friend class CodeEditor;

public:
	Event<int> WhenBreakpoint;
	Event<>    WhenAnnotationMove;
	Event<>    WhenAnnotationClick;
	Event<>    WhenAnnotationRightClick;

	void InsertLines(int i, int count);
	void RemoveLines(int i, int count);
	void ClearLines();

	void Scroll()                          { Refresh(); }

	void SyncSize();

	void Renumber(int linecount);
	void ClearBreakpoints();
	void ValidateBreakpoints();

	String  GetBreakpoint(int ln);
	void    SetBreakpoint(int ln, const String& s);
	void    SetEdited(int ln, int count = 1);
	void    ClearEdited();

	void SetEditor(CodeEditor *e)           { editor = e; }

	LineInfo GetLineInfo() const;
	void     SetLineInfo(const LineInfo& li, int total);
	LineInfoRem & GetLineInfoRem()                   { return li_removed; }
	void     SetLineInfoRem(LineInfoRem pick_ li)    { li_removed = pick(li); }
	
	void     ClearAnnotations();
	void     SetAnnotation(int line, const Image& img, const String& ann);
	String   GetAnnotation(int line) const;

	int      GetLineNo(int lineno) const;
	int      GetNoLine(int line) const;

	void     SetPtr(int line, const Image& img, int i);
	void     HidePtr();

	void     EnableBreakpointing(bool b)     { bingenabled = b; }
	void     HiliteIfEndif(bool b)           { hilite_if_endif = b; Refresh(); }
	void     LineNumbers(bool b);
	void     Annotations(int width);
	void     NoAnnotations(bool b);
	
	bool     IsHiliteIfEndif() const         { return hilite_if_endif; }
	
	int      GetActiveAnnotationLine() const { return active_annotation; }
	
	void     SetAnimate(const Vector<Color>& a)   { if(a != animate) { animate = clone(a); Refresh(); } }
	
	void     StatusImage(const Image& m);
	
	void     Background(Color c)             { if(c != bg_color) { bg_color = c; Refresh(); }}
	void     Text(const String& s, Color c)  { if(s != text || c != text_color)  { text = s; text_color = c; Refresh(); }}

	EditorBar();
	virtual ~EditorBar();
};

struct IdentPos {
	int    begin;
	int    end;
	String ident;
};

Array<IdentPos> GetLineIdent(const char *line);
Vector<Point>   GetLineString(const wchar *wline, bool& is_begin, bool& is_end);

inline int  CharFilterCIdent(int i)  { return IsAlNum(i) || i == '_' ? i : 0; }
inline bool iscidl(int c)            { return iscid(c) || IsLetter(c); }
inline bool islbrkt(int c)           { return c == '{' || c == '[' || c == '('; }
inline bool isrbrkt(int c)           { return c == '}' || c == ']' || c == ')'; }
inline bool isbrkt(int c)            { return islbrkt(c) || isrbrkt(c); }

struct FindReplaceDlg : FrameBottom< WithIDEFindReplaceLayout<TopWindow> > {
	WString itext;
	bool    replacing;

	virtual bool Key(dword key, int count);
	void Setup(bool doreplace);
	void Sync();
	bool IsIncremental() const              { return incremental.IsEnabled() && incremental; }
	
	typedef FindReplaceDlg CLASSNAME;

	FindReplaceDlg();
};

#include "Syntax.h"
#include "CSyntax.h"
#include "DiffSyntax.h"
#include "TagSyntax.h"
#include "LogSyntax.h"
#include "PythonSyntax.h"

class CodeEditor : public LineEdit, public HighlightSetup
{
	friend class EditorBar;

public:
	virtual bool  Key(dword code, int count);
	virtual void  LeftDown(Point p, dword keyflags);
	virtual void  LeftDouble(Point p, dword keyflags);
	virtual void  LeftTriple(Point p, dword keyflags);
	virtual void  LeftRepeat(Point p, dword keyflags);
	virtual void  MouseMove(Point p, dword keyflags);
	virtual Image CursorImage(Point p, dword keyflags);
	virtual void  Serialize(Stream& s);
	virtual void  MouseLeave();
	virtual void  MouseWheel(Point p, int zdelta, dword keyFlags);
	virtual void  Layout();
	virtual void  Paint(Draw& w);

public:
	struct MouseTip {
		int            pos;
		Value          value;
		const Display *display;
		Size           sz;
		bool           delayed = false;
		Color          background;
	};

protected:
	virtual void HighlightLine(int line, Vector<LineEdit::Highlight>& h, int64 pos);
	virtual void PreInsert(int pos, const WString& s);
	virtual void PostInsert(int pos, const WString& s);
	virtual void PreRemove(int pos, int size);
	virtual void PostRemove(int pos, int size);
	virtual void DirtyFrom(int line);
	virtual void SelectionChanged();

	virtual void ClearLines();
	virtual void InsertLines(int line, int count);
	virtual void RemoveLines(int line, int count);

	virtual void NewScrollPos();

	virtual String  GetPasteText();
	
	TimeCallback delayed, closetip;

	EditorBar   bar;
	Vector<int> line2;

	struct SyntaxPos {
		int    line;
		String data;
		
		void Clear() { line = 0; data.Clear(); }
	};
	
	SyntaxPos   syntax_cache[6];

//	EditorSyntax rm_ins;

	char    rmb;
	int64   highlight_bracket_pos0;
	int64   highlight_bracket_pos;
	bool    bracket_flash;
	int64   bracket_start;

	bool    barline;
	double  stat_edit_time;
	Time    last_key_time;

	bool    auto_enclose;
	bool    mark_lines;
	bool    check_edited;
	bool    persistent_find_replace;
	bool    do_ff_restore_pos;
	bool    withfindreplace;
	bool    wordwrap;
	bool    blk0_header;
	
	bool    console_mode = false;

	int     ff_start_pos;

	FindReplaceDlg findreplace;
	
	enum {
		WILDANY = 16,
		WILDONE,
		WILDSPACE,
		WILDNUMBER,
		WILDID,
	};

	struct Found {
		int     type;
		WString text;
	};

	Array<Found> foundwild;
	WString      foundtext;
	bool   foundsel;
	bool   found, notfoundfw, notfoundbk;
	int64  foundpos;
	int    foundsize;
	
	enum { SEL_CHARS, SEL_WORDS, SEL_LINES };
	int    selkind;

	WString illuminated;

	String  iwc;

	String highlight;
	
	int    spellcheck_comments = 0;
	bool   wordwrap_comments = true;
	
	struct Tip : Ctrl {
		Value v;
		Color background;
		const Display *d;
		
		virtual void Paint(Draw& w);
		
		Tip();
	};
	
	bool  delayed_tip = false;
	Point delayed_pos = Null;
	Tip   tip;
	int   tippos = Null;
	
	int   replacei;
	
	bool          search_canceled = false;
	int           search_time0;
	One<Progress> search_progress;
	
	String        refresh_info; // serialized next line syntax context to detect the need of full Refresh

	Vector<Point> errors; // current file (compilation) errors

	struct ScrollBarItems : Ctrl {
		ScrollBar& sb;
		CodeEditor& editor;
	
		void Paint(Draw& w);
		
		Vector<Tuple<int, Image, Color>> pos;
		
		ScrollBarItems(ScrollBar& sb, CodeEditor& e);
	};
	
	ScrollBarItems sbi;


	struct HlSt;
	
	bool   MouseSelSpecial(Point p, dword flags);
	void   InitFindReplace();
	void   CancelBracketHighlight(int64& pos);
	void   FindPrevNext(bool prev);
	void   CheckBrackets();
	void   OpenNormalFindReplace0(bool replace);
	void   OpenNormalFindReplace(bool replace);
	void   FindReplaceAddHistory();
	void   FindWildcard();
	void   ReplaceWildcard();
	void   InsertWildcard(const char *s);
	void   IncrementalFind();
	void   NotFound();
	void   NoFindError();
	void   CheckSyntaxRefresh(int64 pos, const WString& text);
	void   RefreshBlkHeader();

	void   SetFound(int fi, int type, const WString& text);

	int     Match(const wchar *f, const wchar *s, int line, bool we, bool icase, int fi = 0);
	WString GetWild(int type, int& i);
	WString GetReplaceText();

	bool   InsertRS(int chr, int count = 1);

	void   IndentInsert(int chr, int count);

	void   ForwardWhenBreakpoint(int i);

	bool   ToggleSimpleComment(int &start_line, int &end_line, bool usestars = true);
	void   ToggleLineComments(bool usestars = false);
	void   ToggleStarComments();
	void   Enclose(const char *c1, const char *c2, int l = -1, int h = -1);
	void   TabsOrSpaces(String& out, bool maketabs);
	void   LineEnds(String& out);

	enum {
		TIMEID_PERIODIC = Ctrl::TIMEID_COUNT,
		TIMEID_COUNT,
	};

	void   Periodic();

	void   StartSearchProgress(int64 l, int64 h);
	bool   SearchProgress(int line);
	bool   SearchCanceled();
	void   EndSearchProgress();
	bool   SyncCloseTip();

	String GetRefreshInfo(int pos);

public:
	Event<>            WhenSelection;
	Gate<MouseTip&>    WhenTip;
	Event<>            WhenLeftDown;
	Event<int64>       WhenCtrlClick;
	Event<>            WhenAnnotationMove;
	Event<>            WhenAnnotationClick;
	Event<>            WhenAnnotationRightClick;
	Event<>            WhenOpenFindReplace;
	Event<String&>     WhenPaste;
	Event<>            WhenUpdate;
	Event<int>         WhenBreakpoint;

	Event<const Vector<Tuple<int64, int>>&> WhenFindAll;

	FrameTop<Button>    topsbbutton;
	FrameTop<Button>    topsbbutton1;

	virtual bool DelayedTip(MouseTip& tip);

	static dword find_next_key;
	static dword find_prev_key;
	static dword replace_key;

	void   Clear();

	void   Highlight(const String& h);
	String GetHighlight() const       { return highlight; }

	void   EscapeFindReplace();
	void   CloseFindReplace();
	void   FindReplace(bool pick_selection, bool pick_text, bool replace);
	void   FindAll();
	bool   FindFrom(int64 pos, bool back, bool block);
	bool   RegExpFind(int64 pos, bool block);
	bool   Find(bool back, bool block);
	bool   Find(bool back, bool blockreplace, bool replace);
	void   FindNext();
	void   FindPrev();
	bool   GetStringRange(int64 cursor, int64& b, int64& e) const;
	bool   GetStringRange(int64& b, int64& e) const { return GetStringRange(GetCursor64(), b, e); }
	bool   FindString(bool back);
	bool   FindLangString(bool back);
	void   Replace();
	void   ReplaceAll(bool rest);
	int    BlockReplace();

	void   MakeTabsOrSpaces(bool tabs);
	void   MakeLineEnds();

	void   CopyWord();
	void   SwapChars();
	void   DuplicateLine();
	void   Put(int chr);
	void   FinishPut();

	void   SerializeFind(Stream& s);
	bool   IsFindOpen() const                       { return findreplace.IsOpen(); }
	void   FindClose()                              { CloseFindReplace(); }

	void   Goto();

	void   DoFind();
	void   DoFindBack();

//	void    FindWord(bool back);
	WString GetI();
	void    SetI(Ctrl *edit);
	void    PutI(WithDropChoice<EditString>& edit);

	void   MoveNextWord(bool sel);
	void   MovePrevWord(bool sel);
	void   MoveNextBrk(bool sel);
	void   MovePrevBrk(bool sel);

	String GetWord(int64 pos);
	String GetWord();

	bool   GetWordPos(int64 pos, int64& l, int64& h);

	void   DeleteWord();
	void   DeleteWordBack();
	void   SetLineSelection(int l, int h);
	bool   GetLineSelection(int& l, int& h);
	void   TabRight();
	void   TabLeft();
	void   SwapUpDown(bool up);

	void   Make(Event<String&> op);

	void    CheckEdited(bool e = true)                { check_edited = e; }
	bool    GetCheckEdited()                          { return check_edited; }

	void    EditorBarLayout()                         { bar.SyncSize(); }

	LineInfo GetLineInfo() const                      { return bar.GetLineInfo(); }
	void     SetLineInfo(const LineInfo& lf);
	LineInfoRem GetLineInfoRem()                      { return LineInfoRem(bar.GetLineInfoRem(), 0); }
	void     SetLineInfoRem(LineInfoRem pick_  lf)    { bar.SetLineInfoRem(LineInfoRem(lf, 0)); }
	double   GetStatEditTime() const                  { return stat_edit_time; }
	void     Renumber()                               { bar.Renumber(GetLineCount()); }
	void     ClearBreakpoints()                       { bar.ClearBreakpoints(); }
	void     ValidateBreakpoints()                    { bar.ValidateBreakpoints(); }
	int      GetLineNo(int line) const                { return bar.GetLineNo(line); }
	int      GetNoLine(int line) const                { return bar.GetNoLine(line); }
	void     SetPtr(int line, const Image& img, int i){ bar.SetPtr(line, img, i); }
	void     HidePtr()                                { bar.HidePtr(); }
	String   GetBreakpoint(int line)                  { return bar.GetBreakpoint(line); }
	void     SetBreakpoint(int line, const String& b) { bar.SetBreakpoint(line, b); }
	void     ClearEdited()                            { bar.ClearEdited(); }
	int		 GetUndoCount()                           { return undo.GetCount(); }
	void     GotoBarLine(int line);
	void     EnableBreakpointing()                    { bar.EnableBreakpointing(true); }
	void     DisableBreakpointing()                   { bar.EnableBreakpointing(false); }
	void     Renumber2();
	int      GetLine2(int i) const;
	void     ReformatComment();

// TODO: Syntax: Remove
	void     HiliteScope(byte b)                      { EditorSyntax::hilite_scope = b; Refresh(); }
	void     HiliteBracket(byte b)                    { EditorSyntax::hilite_bracket = b; Refresh(); }
	void     HiliteIfDef(byte b)                      { EditorSyntax::hilite_ifdef = b; Refresh(); }
	void     HiliteIfEndif(bool b)                    { bar.HiliteIfEndif(b); }

	void     ThousandsSeparator(bool b)               { thousands_separator = b; Refresh(); }
	void     IndentSpaces(bool is)                    { indent_spaces = is; }
	void     IndentAmount(int ia)                     { indent_amount = ia; }
	void     NoParenthesisIndent(bool b)              { no_parenthesis_indent = b; }
	
	void     SpellcheckComments(int lang)             { spellcheck_comments = lang; Refresh(); }
	int      GetSpellcheckComments() const            { return spellcheck_comments; }
	void     WordwrapComments(bool b)                 { wordwrap_comments = b; }
	bool     IsWordwrapComments() const               { return wordwrap_comments; }

	void     NoFindReplace()                          { withfindreplace = false; }

	void     LineNumbers(bool b)                      { bar.LineNumbers(b); }
	void     MarkLines(bool b)                        { mark_lines = b; }
	bool     GetMarkLines()                           { return mark_lines; }
	void     AutoEnclose(bool b)                      { auto_enclose = b; }
	void     BarLine(bool b)                          { barline = b; }
	void     WordWrap(bool b)                         { wordwrap = b; }
	void     Blk0Header(bool b)                       { blk0_header = b; Refresh(); }
	
	void     PersistentFindReplace(bool b = true)     { persistent_find_replace = b; }
	bool     IsPersistentFindReplace() const          { return persistent_find_replace; }

	void     FindReplaceRestorePos(bool b = true)     { do_ff_restore_pos = b; }
	bool     IsFindReplaceRestorePos() const          { return do_ff_restore_pos; }
	
	void     Annotations(int width)                   { bar.Annotations(width); }
	void     ClearAnnotations()                       { bar.ClearAnnotations(); }
	void     SetAnnotation(int i, const Image& icon, const String& a) { bar.SetAnnotation(i, icon, a); }
	String   GetAnnotation(int i) const               { return bar.GetAnnotation(i); }
	int      GetActiveAnnotationLine() const          { return bar.GetActiveAnnotationLine(); }
	Size     GetBarSize() const                       { return bar.GetSize(); }
	void     HideBar()                                { bar.Hide(); }
	void     ConsoleMode()                            { console_mode = true; }
	void     AnimateBar(const Vector<Color>& a)       { bar.SetAnimate(a); }
	void     BarColor(Color c)                        { bar.Background(c); }
	void     BarText(const String& text, Color c)     { bar.Text(text, c); }
	
	void     NoAnnotations(bool b = true)             { bar.NoAnnotations(b); }

	void     Errors(Vector<Point>&& errs);
	
	void     Illuminate(const WString& text);
	WString  GetIlluminated() const                   { return illuminated; }
	
	void     Zoom(int d);
	
	void     StatusImage(const Image& m)              { bar.StatusImage(m); }

	One<EditorSyntax> GetSyntax(int line);
	bool IsCursorBracket(int64 pos) const;
	bool IsMatchingBracket(int64 pos) const;

// TODO: Do we really need this ?
	Vector<IfState> GetIfStack(int line)              { return GetSyntax(line)->PickIfStack(); }

	struct FindReplaceData {
		String find, replace;
		String find_list, replace_list;
		bool   wholeword, wildcards, ignorecase, samecase, regexp;
	};
	
	FindReplaceData GetFindReplaceData();
	void            SetFindReplaceData(const FindReplaceData& d);

	void     SyncTip();
	void     CloseTip();

	typedef CodeEditor CLASSNAME;

	CodeEditor();
	virtual ~CodeEditor();

	static void InitKeywords();
};

String ReadList(WithDropChoice<EditString>& e);
void   WriteList(WithDropChoice<EditString>& e, const String& data);

}

#endif
