#include "RichText.h"

NAMESPACE_UPP

RichTable::Format::Format()
{
	grid = 4;
	gridcolor = Black;
	frame = 10;
	framecolor = Black;
	before = after = lm = rm = 0;
	header = 0;
	keep = false;
}

#include "RichText.h"

void RichTable::ExpandFrr(VectorMap<int, Rect>& frr, int pi, int l, int r, int t, int b)
{
	Rect& fr = frr.GetAdd(pi, Rect(INT_MAX, INT_MAX, INT_MIN, INT_MIN));
	fr.left = min(fr.left, l);
	fr.right = max(fr.right, r);
	fr.top = min(fr.top, t);
	fr.bottom = max(fr.bottom, b);
}

bool RichTable::RowPaint(PageDraw& pw, const RichStyles& st, const Layout& tab,
                          int i, int ny, const Rect& pg, VectorMap<int, Rect>& frr,
                          PaintInfo& pi, int pd, bool sel) const
{
	RichContext rc(st);
	const Array<RichCell>& row = cell[i];
	const PaintRow& pr = tab[i];
	rc.py = pr.py;
	rc.py.page += pd;
	int gridln = LineZoom(pi.zoom, format.grid);
	int ff2ln = gridln - gridln / 2;
	Color gc = format.gridcolor;
	if(gridln == 0 && !IsNull(pi.showcodes)) {
		gridln = 1;
		gc = pi.showcodes;
	}
	for(int j = 0; j < format.column.GetCount();) {
		const RichCell& cell = row[j];
		const PaintCell& pc = pr[j];
		if(pc.top) {
			PageY pyy = tab[min(ny - 1, i + cell.vspan)].pyy;
			pyy.page += pd;
			bool paint = pyy > pi.top;
			rc.page = pr[j].page;
			Rect xpg = pg;
			int y;
			int ny = pi.zoom * pyy.y;
			y = pi.zoom * pr.gpy.y;
			if(j) {
				xpg.left = pi.zoom * pc.left - ff2ln;
				if(pyy.page == rc.py.page)
					pw.Page(rc.py.page).DrawRect(xpg.left, y, gridln, ny - y, gc);
				else {
					pw.Page(rc.py.page).DrawRect(xpg.left, y, gridln, pg.bottom - y, gc);
					for(int i = rc.py.page + 1; i < pyy.page; i++)
						pw.Page(i).DrawRect(xpg.left, pg.top, gridln, pg.bottom - pg.top, gc);
					pw.Page(pyy.page).DrawRect(xpg.left, pg.top, gridln, ny - pg.top, gc);
				}
				xpg.left += gridln;
			}
			if(j + cell.hspan < format.column.GetCount() - 1)
				xpg.right = pi.zoom * pc.right - ff2ln;
			if(!pr.first) {
				y += gridln;
				if(paint)
					pw.Page(rc.py.page).DrawRect(xpg.left, y - gridln, xpg.Width(), gridln, gc);
			}
			if(paint)
				row[j].Paint(pw, rc, pyy, xpg, y, ny, pi,
				             sel && j >= pi.cells.left && i >= pi.cells.top &&
				             j + cell.hspan <= pi.cells.right && i + cell.vspan <= pi.cells.bottom);
			if(pyy.page == rc.py.page)
				ExpandFrr(frr, pyy.page, xpg.left, xpg.right, y, ny);
			else {
				ExpandFrr(frr, rc.py.page, xpg.left, xpg.right, y, xpg.bottom);
				for(int i = rc.py.page + 1; i < pyy.page; i++)
					ExpandFrr(frr, i, xpg.left, xpg.right, xpg.top, xpg.bottom);
				ExpandFrr(frr, pyy.page, xpg.left, xpg.right, xpg.top, ny);
			}
			int l = cell.text.GetLength() + 1;
			pi.sell -= l;
			pi.selh -= l;
			pi.tablesel -= cell.text.GetTableCount();
		}
		j += cell.hspan + 1;
	}

	return rc.py >= pi.bottom;
}

void RichTable::Paint(PageDraw& pw, RichContext rc, const PaintInfo& _pi) const
{
	const TabLayout& tab = Realize(rc);
	if(tab.page.IsEmpty())
		return;
	Rect p = tab.page;
	PaintInfo pi = _pi;
	int frameln = LineZoom(pi.zoom, format.frame);
	int gridln = LineZoom(pi.zoom, format.grid);
	Rect pg = rc.page;
	pg.left += format.lm;
	pg.right -= format.rm;
	pg.left = pi.zoom * pg.left;
	pg.right = pi.zoom * pg.right;
	pg.top = pi.zoom * pg.top;
	pg.bottom = pi.zoom * pg.bottom;
	pg.Deflate(frameln);
	int hy = min(format.header, cell.GetCount());
	Rect hpg = pg;
	if(tab.hasheader) {
		hpg.bottom = pi.zoom * tab.header[hy - 1].pyy.y;
		pg.top = hpg.bottom + gridln;
	}
	bool allsel = false;
	if(pi.sell < 0 && pi.selh >= 0) {
		pi.sell = pi.selh = 0;
		allsel = true;
	}
	bool sel = pi.tablesel == 0;
	int ny = cell.GetCount();
	VectorMap<int, Rect> frr;
	for(int i = 0; i < ny; i++)
		if(RowPaint(pw, rc.styles, tab, i, ny, pg, frr, pi, 0, sel))
			break;

	Color gc = format.gridcolor;
	Color fc = format.framecolor;
	int fl = frameln;
	if(!IsNull(pi.showcodes)) {
		if(fl == 0 && !IsNull(pi.showcodes)) {
			fl = 1;
			fc = pi.showcodes;
		}
		if(gridln == 0) {
			gridln = 1;
			gc = pi.showcodes;
		}
	}
	for(int i = 0; i < frr.GetCount(); i++) {
		PaintInfo _pi = pi;
		pi.tablesel = 0;
		pi.sell = pi.selh = -1;
		int pgi = frr.GetKey(i);
		Draw& w = pw.Page(pgi);
		if(pgi > tab.page0 && tab.hasheader)
			for(int i = 0; i < hy; i++) {
				RowPaint(pw, rc.styles, tab.header, i, hy, hpg, frr, pi, pgi, false);
				w.DrawRect(pg.left, hpg.bottom, pg.Width(), gridln, format.gridcolor);
			}
		Rect r = frr[i].Inflated(frameln);
		if(!r.IsEmpty()) {
			DrawFatFrame(w, r, fc, fl);
			if(allsel)
				w.DrawRect(r, InvertColor);
		}
	}
}

PageY RichTable::GetHeight(RichContext rc) const
{
	PageY pyy = Realize(rc).pyy;
	pyy.y += format.frame;
	pyy.y += format.after;
	if(pyy.y > rc.page.bottom) {
		pyy.y = rc.page.top;
		pyy.page++;
	}
	return pyy;
}

PageY RichTable::GetTop(RichContext rc) const
{
	if(cell.GetCount() == 0)
		return rc.py;
	rc.py = Realize(rc)[0].py;
	rc.py.y -= format.frame;
	return rc.py;
}

RichCaret RichTable::GetCaret(int pos, RichContext rc) const
{
	ASSERT(pos >= 0);
	int nx = format.column.GetCount();
	int ny = cell.GetCount();
	int ti = 0;
	const TabLayout& tab = Realize(rc);
	for(int i = 0; i < ny; i++) {
		const PaintRow& pr = tab[i];
		rc.py = pr.py;
		PageY pyy;
		for(int j = 0; j < nx; j++) {
			if(ci[i][j].valid) {
				const RichCell& cl = cell[i][j];
				pyy = tab[min(ny - 1, i + cl.vspan)].pyy;
				int l = cl.text.GetLength() + 1;
				if(pos < l) {
					rc.page = pr[j].page;
					return cl.GetCaret(pos, rc, pyy);
				}
				ti += cl.text.GetTableCount();
				pos -= l;
			}
		}
	}
	NEVER();
	return RichCaret();
}

int  RichTable::GetPos(int x, PageY y, RichContext rc) const
{
	int nx = format.column.GetCount();
	int ny = cell.GetCount();
	const TabLayout& tab = Realize(rc);
	int pos = 0;
	for(int i = 0; i < ny; i++) {
		const PaintRow& pr = tab[i];
		rc.py = pr.py;
		for(int j = 0; j < nx; j++)
			if(ci[i][j].valid) {
				const RichCell& cl = cell[i][j];
				const PaintCell& pc = pr[j];
				PageY pyy = tab[min(ny - 1, i + cl.vspan)].pyy;
				if(y < pyy
				   && (j == 0 || x >= pc.page.left - format.grid)
				   && (j == nx - 1 || x < pc.page.right)) {
					rc.page = pc.page;
					return cl.GetPos(x, y, rc, pyy) + pos;
				}
				pos += cl.text.GetLength() + 1;
			}
	}
	return pos ? pos - 1 : 0;
}

RichHotPos  RichTable::GetHotPos(int x, PageY y, int tolerance, RichContext rc) const
{
	RichHotPos hp;
	hp.textleft = rc.page.left;
	hp.textcx = rc.page.Width();
	int nx = format.column.GetCount();
	int ny = cell.GetCount();
	const TabLayout& tab = Realize(rc);
	if(abs(x - tab.page.left) <= tolerance) {
		hp.table = 0;
		hp.column = RICHHOT_LM;
		hp.delta = x - tab.page.left;
		return hp;
	}
	if(abs(x - tab.page.right) <= tolerance) {
		hp.table = 0;
		hp.column = RICHHOT_RM;
		hp.delta = x - tab.page.right;
		return hp;
	}
	int ti = 0;
	for(int i = 0; i < ny; i++) {
		const PaintRow& pr = tab[i];
		rc.py = pr.py;
		for(int j = 0; j < nx; j++) {
			if(ci[i][j].valid) {
				const RichCell& cl = cell[i][j];
				const PaintCell& pc = pr[j];
				PageY pyy = tab[min(ny - 1, i + cl.vspan)].pyy;
				if(y < pyy) {
					if(j < nx - 1 && abs(x - pc.page.right) <= tolerance) {
						hp.table = 0;
						hp.column = j + cl.hspan;
						hp.delta = x - pc.page.right;
						hp.left = tab.page.left;
						hp.cx = tab.page.Width();
						return hp;
					}
					if((j == 0 || x >= pc.page.left - format.grid) && (j == nx - 1 || x < pc.page.right)) {
						rc.page = pc.page;
						hp = cl.GetHotPos(x, y, tolerance, rc, pyy);
						hp.table += ti;
						return hp;
					}
				}
				ti += cl.text.GetTableCount();
			}
		}
	}
	return RichHotPos();
}

int RichTable::GetVertMove(int pos, int gx, RichContext rc, int dir) const
{
	if(cell.GetCount() == 0)
		return -1;
	int nx = format.column.GetCount();
	int ny = cell.GetCount();
	const TabLayout& tab = Realize(rc);
	Point cp;
	if(pos >= 0)
		cp = FindCell(pos);
	else {
		cp.y = dir > 0 ? 0 : cell.GetCount() - 1;
		cp.x = 0;
		const PaintRow& pr = tab[cp.y];
		for(int j = 0; j < nx; j++)
			if(ci[cp.y][cp.x].valid && gx < pr[j].page.right) {
				cp.x = j;
				break;
			}
		pos = -1;
	}
	for(;;) {
		rc.page = tab[cp.y][cp.x].page;
		const RichCell& c = cell[cp.y][cp.x];
		RichContext rc1 = rc;
		if(c.Reduce(rc1)) {
			int q = cell[cp.y][cp.x].text.GetVertMove(pos, gx, rc1, dir);
			if(q >= 0)
				return q + GetCellPos(cp);
		}
		pos = -1;
		cp.y += dir;
		if(cp.y < 0 || cp.y >= ny)
			return -1;
	}
}

void RichTable::GatherValPos(Vector<RichValPos>& f, RichContext rc, int pos, int type) const
{
	ASSERT(pos >= 0);
	int nx = format.column.GetCount();
	int ny = cell.GetCount();
	const TabLayout& tab = Realize(rc);
	for(int i = 0; i < ny; i++) {
		const PaintRow& pr = tab[i];
		rc.py = pr.py;
		for(int j = 0; j < nx; j++)
			if(ci[i][j].valid) {
				const RichCell& cl = cell[i][j];
				rc.page = pr[j].page;
				cl.GatherValPos(f, rc, tab[min(ny - 1, i + cl.vspan)].pyy, pos, type);
				pos += cl.text.GetLength() + 1;
			}
	}
}

void RichTable::ApplyZoom(Zoom z, const RichStyles& ostyle, const RichStyles& zstyle)
{
	Invalidate();
	r_row = -1;
	int nx = format.column.GetCount();
	int ny = cell.GetCount();
	format.frame = LineZoom(z, format.frame);
	format.grid = LineZoom(z, format.grid);
	for(int i = 0; i < ny; i++)
		for(int j = 0; j < nx; j++)
			if(ci[i][j].valid) {
				RichCell& c = cell[i][j];
				c.format.border *= z;
				c.format.margin *= z;
				c.text.ApplyZoom(z, ostyle, zstyle);
			}
}

END_UPP_NAMESPACE
