#include <CtrlLib/CtrlLib.h>
#include <RichText/RichText.h>

struct App : public TopWindow {
	Zoom         zoom;
	Rect         page;
	int          cursor, anchor;
	int          osell, oselh;
	RichText     text;
	int          table;
	Rect         tabsel;
	Label        hotpos, pos, rpos;
	RichText     clip;
	RichText::Formating formating;
	RichCaret    cursorc;
	RichPos      cursorp;
	StatusBar    status;

	ScrollBar sb;

	void Scroll();

	virtual void Paint(Draw& w);
	virtual void Layout();
	virtual void LeftDown(Point p, dword);
	virtual bool Key(dword key, int);
	virtual void MouseMove(Point p, dword);

	void   Finish();
	int    GetPosY(PageY py);

	typedef App CLASSNAME;

	App();
};

enum { PAGES = 300, PAGECY = 300 };

struct AppPageDraw : public PageDraw {
	virtual Draw& Page(int _page);
	virtual Draw& Info()                   { return w; }

	Draw&              w;
	int                page;
	Size               size;

	AppPageDraw(Draw& w) : w(w) { w.Begin(); page = -1; }
	~AppPageDraw() { w.End(); }
};

Draw& AppPageDraw::Page(int _page)
{
	if(page == _page) return w;
	page = _page;
	w.End();
	w.Clipoff(0, (size.cy + 2) * page, size.cx, size.cy);
	return w;
}

void Test(PageDraw& ww)
{
	for(int i = 0; i < 10; i++) {
		Draw& w = ww.Page(i);
		w.DrawText(0, 0, AsString(i), Arial(80));
	}
}

int  App::GetPosY(PageY py)
{
	return py.page * (zoom * page.Height()) + py.y * zoom + py.page * 2;
}

void App::Finish()
{
	if(anchor != cursor)
		cursor = text.AdjustCursor(anchor, cursor);
	RichCaret pr = text.GetCaret(cursor, page);
//	if(pr.table)
//		rpos = AsString(text.GetCellPos(pr.table, pr.row, pr.column));
//	else
	status = AsString(cursor) + ": " + AsString(text.GetRichPos(cursor));
	int h = pr.Height();
	SetCaret(RectC(pr.left * zoom, GetPosY(pr) + (pr.lineascent - pr.caretascent) * zoom - sb,
	               2, (pr.caretascent + pr.caretdescent) * zoom));
	PageY top, bottom;
//	RichPos tp = pr;
//	pos = AsString(tp);
	int sell = min(cursor, anchor);
	int selh = max(cursor, anchor);
	if(text.GetInvalid(top, bottom, page, sell, selh, osell, oselh)) {
		int y = GetPosY(top);
		int yy = GetPosY(bottom);
		Refresh(0, y, GetSize().cx, yy - y);
		text.Validate();
		osell = sell;
		oselh = selh;
	}
//	Refresh();
}

void App::LeftDown(Point p, dword)
{
	PageY py;
	int x = p.x / zoom;
	py.page = p.y / (PAGECY + 2);
	py.y = (p.y % (PAGECY + 2)) / zoom;
	anchor = cursor = text.GetPos(x, py, page);
	Finish();
}

void App::MouseMove(Point p, dword)
{
	PageY py;
	int x = p.x / zoom;
	py.page = p.y / (PAGECY + 2);
	py.y = (p.y % (PAGECY + 2)) / zoom;
	RichHotPos pos = text.GetHotPos(x, py, 50, page);
	hotpos = Format("table: %d, column: %d", pos.table, pos.column);
}

void App::Paint(Draw& w)
{
	Size sz = GetSize();

	w.DrawRect(sz, SWhite);

	w.Offset(0, -sb);
	AppPageDraw ww(w);
	ww.size = Size(GetSize().cx, PAGECY);
	for(int i = 0; i < PAGES; i++)
		w.DrawRect(0, (ww.size.cy + 2) * i - 2, sz.cx, 2, SLtBlue);

	PaintInfo pi;
	pi.zoom = Zoom(100, 1000);

	pi.top = PageY((int)sb / (ww.size.cy + 2), 0);
	pi.bottom = PageY(((int)sb + sz.cy) / (ww.size.cy + 2) + 1, 0);

	pi.usecache = true;

	pi.tablesel = table;
	pi.cells = tabsel;

	pi.sell = min(cursor, anchor);
	pi.selh = max(cursor, anchor);

	text.Paint(ww, page, pi);

	w.End();
}

void App::Layout()
{
	sb.SetPage(GetSize().cy);
	Refresh();
}

void App::Scroll()
{
	Refresh();
}

bool App::Key(dword key, int)
{
	RichPos tp = cursorp;
	if(key == K_LEFT && cursor > 0) {
		cursor--;
		anchor = cursor;
	}
	if(key == K_RIGHT && cursor < text.GetLength()) {
		cursor++;
		anchor = cursor;
	}
	if(key == K_SHIFT_RIGHT && cursor < text.GetLength()) {
		cursor++;
	}
	if(key == K_SHIFT_LEFT && cursor > 0) {
		cursor--;
	}
	if(key == K_CTRL_L) {
		table = tp.table;
		tabsel.left = tp.cell.x;
		tabsel.top = tp.cell.y;
	}
	if(key == K_CTRL_H) {
		table = tp.table;
		tabsel.right = tp.cell.x;
		tabsel.bottom = tp.cell.y;
	}
	if(key == K_DELETE)
		text.Remove(cursor, 1);
	if(key >= ' ' && key <= 65000 || key == 9) {
		RichPara p;
		p.Cat(WString(key, 1), RichPara::Format());
		RichText txt;
		txt.SetStyles(text.GetStyles());
		txt.Set(0, p, text.GetStyles());
		text.Insert(cursor++, txt);
		anchor = cursor;
	}
	if(key == K_CTRL_C) {
		clip = text.Copy(min(cursor, anchor), abs(cursor - anchor));
	}
	if(key == K_CTRL_V) {
		RichPara p;
		text.Insert(cursor++, clip);
	}
	if(key == K_CTRL_T) {
		RichPara p;
		p.Cat(WString('X', 1), RichPara::Format());
		RichText txt;
		txt.SetStyles(text.GetStyles());
		txt.Set(0, p, text.GetStyles());
		text.Insert(cursor++, txt);
		anchor = cursor;
	}
	if(key == K_CTRL_R) {
		formating = text.SaveFormat(min(cursor, anchor), abs(cursor - anchor));
		RichText::FormatInfo f;
		(Font&)f = Courier(250);
		f.charvalid = RichText::FACE|RichText::HEIGHT;
		f.paravalid = 0;
		text.ApplyFormatInfo(min(cursor, anchor), f, abs(cursor - anchor));
	}
	if(key == K_CTRL_Z) {
		text.RestoreFormat(min(cursor, anchor), formating);
	}
	Finish();
	return true;
}

App::App()
{
	AddFrame(sb);
	sb.SetTotal(30 * PAGES);
	sb.WhenScroll = THISBACK(Scroll);
	Sizeable().Zoomable();

	zoom = Zoom(100, 1000);
	page = Rect(0, 0, 1000 / zoom, PAGECY / zoom);

	text = ParseQTF("1&2&3&41&2&3&41&2&3&41&2&3&41&2&3&41&2&3&41&2&3&41&2&3&41&2&3&41&2&3&4");
/*
	RichTable table = GenTab(7, 7);
	{
		RichText h;
		h.SetPick(0, GenTab(3, 3));
		table.SetPick(0, 2, h);
	}
	{
		RichText h;
		h.SetPick(0, GenTab(3, 3));
		table.SetPick(2, 3, h);
	}

	RichTable::Format fmt = table.GetFormat();
	fmt.lm = 200;
	fmt.rm = 300;
	table.SetFormat(fmt);

	text.SetPick(4, table);
*/
	BackPaint();
	sb.SetLine(32);
//	sb = 320;

	cursor = anchor = 0;
	Maximize();
/*
	this->table = 1;
	tabsel.left = 0;
	tabsel.top = 0;
	tabsel.right = 2;
	tabsel.bottom = 3;
*/

/*
	hotpos.NoTransparent();
	Add(hotpos.BottomPos(0, 20).RightPos(0, 200));
	pos.NoTransparent();
	Add(pos.TopPos(0, 20).RightPos(0, 500));
	rpos.NoTransparent();
	Add(rpos.TopPos(20, 20).RightPos(0, 500));
*/

	AddFrame(status);

	text = ParseQTF(
		"1&2&3&4"
		"1&2&3&4"
		"++A||B||C--X||Y++"
		"{{1:1@3-1 A:: ::! C::a30/40 {{1:1:1 1:: 2:: 3}}::! E&E1:: F {{1:1:1~ A:: B:: C}}}}"
		"1&2&3&4"
		"1&2&3&4"
		"1&2&3&4"
		"1&2&3&4"
	);

	DUMP(AsQTF(text));
}

GUI_APP_MAIN
{
	Ctrl::ShowRepaint = 10;

	App p;
	p.Run();
	p.text = ParseQTF(AsQTF(p.text));
	DUMP(AsQTF(p.text));
//	p.Finish();
//	p.Run();
}
