#include "GridCtrl.h"

namespace Upp {
	
#define LLOG(x) // DLOG(x)

void GridCtrl::Paint(Draw &w)
{
	[[maybe_unused]] static int paintcnt = 0;

	Font stdfont(StdFont());

	Size sz = GetSize();
	Rect rc = Rect(sz);  //w.GetClip() - it always returns view rect now. bug??
	
	if(!ready)
	{
		w.DrawRect(rc, SColorPaper);
		return;
	}
	
	int i, j, cx, cy, x, y;
	bool skip;
	Rect r;

	LLOG("---- Paint " << ++paintcnt);

	if(total_cols <= 1 || total_rows == 0)
	{
		LLOG("---- Paint " << paintcnt << " Empty.");
		w.DrawRect(sz, SColorPaper);
		return;
	}

	if(UpdateCols() || UpdateRows())
		UpdateSizes();

	if(firstCol < 0) firstCol = GetFirstVisCol(fixed_width);
	if(firstRow < 0) firstRow = GetFirstVisRow(fixed_height);

	LLOG("firstCol " << firstCol);
	LLOG("firstRow " << firstRow);

	int en = IsShowEnabled() ? 0 : GD::READONLY;

	//---------------------------------------------------------------------------------------

	if(fixed_width > 0 && fixed_height > 0)
	{
		w.Clip(0, 0, fixed_width, fixed_height);
		dword style = chameleon ? GD::CHAMELEON : 0;

		display->PaintFixed(w, 1, 1, 0, 0, fixed_width, fixed_height,
							Value(""),
							style, stdfont, false, false,
							0, -1, 0,
							true);
		w.End();
	}

	r.Set(fixed_width, 0, sz.cx, summary_row ? sz.cy : fixed_height);

	if(w.IsPainting(r) && total_cols > 1)
	{
		LLOG("Top header");
		w.Clip(r);

		x = hitems[total_cols - 1].nRight(sbx);
		int rx = x;

		int firstcol = indicator ? 0 : (fixed_cols > 1 ? 1 : firstVisCol);
		if(firstCol < 0) firstCol = 0;
		int jfc = chameleon ? 0 : firstCol;

		for(i = 0; i < fixed_rows; i++)
		{
			for(j = jfc; j < total_cols; j++)
			{
				ItemRect& hi = hitems[j];
				
				if(hi.hidden)
					continue;

				int jj = j;
				Item &it = GetItemSize(i, j, x, y, cx, cy, skip, true, false);
				if(skip)
					continue;

				if(x >= rc.right)
					break;

				if(w.IsPainting(x, y, cx, cy))
				{
					GridDisplay * gd = it.display ? it.display : display;

					dword style = hi.style | hi.halign;
					if(i > 0) style &= ~GD::HIGHLIGHT;
					if(chameleon)
						style |= GD::CHAMELEON;

					Font fnt(stdfont);
					gd->SetLeftImage(hi.img);
					gd->ReverseSortIcon(reverse_sort_icon);
					gd->PaintFixed(w, jj == firstcol, i == 0, x, y, cx, cy,
								i == 0 ? it.val : GetConvertedColumn(hi.id, it.val),
								style | en, IsNull(hi.hfnt) ? fnt : hi.hfnt, false, false,
								i == 0 ? hi.sortmode : 0,
								hi.sortcol,
								sortOrder.GetCount(),
								true);
					
					it.rcx = gd->real_size.cx;
					it.rcy = gd->real_size.cy;
				}
				
				if(summary_row && i == 0)
				{
					cy = GD_HDR_HEIGHT;
					y = sz.cy - cy;
				
					if(w.IsPainting(x, y, cx, cy))
					{
						Item &it = summary[hi.id];
						GridDisplay * gd = it.display ? it.display : display;
	
						dword style = en | hi.halign;
						if(chameleon)
							style |= GD::CHAMELEON;
						
						String s;
						if(hi.sop != SOP_NONE && !hi.sopfrm.IsEmpty() && !IsNull(it.val))
							s = Format(hi.sopfrm, it.val);
						else
							s = IsString(it.val) ? it.val : StdConvert().Format(it.val);
						gd->SetLeftImage(Null);
						//gd->PaintFixed(w, jj == firstcol, i == 0, x, y, cx, cy, s,
						//			   style | en, stdfont, false, false, 0, -1, 0, true);
						Color fg = Black;
						Color bg = Blend(Blue, White, 240);
						w.DrawRect(x, y, cx, 1, Gray);
						Font fnt(stdfont);

						if(style & GD::READONLY)
						{
							bg = Blend(bg, SGray(), 40);
							fg = Blend(fg, SGray(), 200);
						}
						
						gd->Paint(w, x, y + 1, cx, cy - 1, s, style, fg, bg, fnt.Bold(), false, 0, 0);
						
						it.rcx = gd->real_size.cx;
						it.rcy = gd->real_size.cy;
					}
				}
			}
		}
		if(rx < sz.cx || chameleon)
		{
			int cx = sz.cx - rx + 1;
			dword style = 0;
			if(chameleon)
			{
				cx = max(10, cx);
				style = GD::CHAMELEON;
			}
			display->PaintFixed(w, 0, 1, rx, 0, cx, fixed_height,
								Value(""),
								style, stdfont, false, false,
								0, -1, 0,
								true);
			if(summary_row)
			{
				Color fg = Black;
				Color bg = Blend(Blue, White, 240);
				w.DrawRect(rx, y, cx, 1, Gray);
				
				if(style & GD::READONLY)
				{
					bg = Blend(bg, SGray(), 40);
					fg = Blend(fg, SGray(), 200);
				}
				display->Paint(w, rx, y + 1, cx, GD_HDR_HEIGHT - 1, Value(""), style, fg, bg, stdfont, false, 0, 0);
			}
		}

		w.End();
	}
	//---------------------------------------------------------------------------------------

	bool can_paint = firstCol >= 0 && firstRow >= 0;
	r.Set(0, fixed_height, fixed_width, sz.cy);

	if(can_paint && w.IsPainting(r))
	{
		LLOG("Left header");
		w.Clip(r);
		y = vitems[total_rows - 1].nBottom(sby);

		if(y < sz.cy)
			w.DrawRect(Rect(0, y, fixed_width, sz.cy - summary_height), SColorPaper);

		for(i = 0; i < fixed_cols; i++)
		{
			bool firstx = (i == !indicator);
			bool indicator = (i == 0);
			int id = hitems[i].id;

			for(j = firstRow; j < total_rows; j++)
			{
				ItemRect& vi = vitems[j];
				
				if(vi.hidden)
					continue;

				Item &it = GetItemSize(j, i, x, y, cx, cy, skip, false, true);

				if(skip)
					continue;

				if(y >= rc.bottom)
					break;

				if(w.IsPainting(x, y, cx, cy))
				{
					GridDisplay * gd = it.display ? it.display : display;

					dword style = vi.style;
					if(i > 0) style &= ~GD::HIGHLIGHT;
					if(chameleon)
						style |= GD::CHAMELEON;

					Font fnt(stdfont);
					gd->PaintFixed(w, firstx, j == 0, x, y, cx, cy,
									GetConvertedColumn(id, it.val),
									style | en, IsNull(vi.hfnt) ? fnt : vi.hfnt,
									indicator, false, 0, -1, 0, false);
					
					it.rcx = gd->real_size.cx;
					it.rcy = gd->real_size.cy;
				}
			}
			
			if(summary_row)
			{
				j = 0;
				cy = GD_HDR_HEIGHT;
				y = sz.cy - cy;
				if(w.IsPainting(x, y, cx, cy))
				{
					Item& it = summary[hitems[i].id];
					GridDisplay * gd = it.display ? it.display : display;

					dword style = vitems[j].style;
					if(chameleon)
						style |= GD::CHAMELEON;

//					gd->PaintFixed(w, firstx, true, x, y, cx, cy,
//									GetConvertedColumn(id, it.val),
//									style | en, stdfont,
//									false, false, 0, -1, 0, false);
//
					Color fg = Black;
					Color bg = Blend(Blue, White, 240);
					w.DrawRect(x, y, cx, 1, Gray);
					Font fnt(stdfont);
					gd->Paint(w, x, y + 1, cx, cy - 1, GetConvertedColumn(id, it.val), style | en, fg, bg, fnt.Bold(), false, 0, 0);

				}
			}
		}

		w.End();
	}

	//---------------------------------------------------------------------------------------
	// Draw the "active" (non-fixed) cells:

	r.Set(fixed_width, fixed_height, sz.cx, sz.cy - summary_height);

	if(can_paint && w.IsPainting(r))
	{
		LLOG("Body");
		w.Clip(r);

		x = hitems[total_cols - 1].nRight(sbx);
		y = vitems[total_rows - 1].nBottom(sby);

		if(x < sz.cx) w.DrawRect(Rect(max(x, rc.left), max(fixed_height, rc.top), sz.cx, sz.cy), SColorPaper);
		if(y < sz.cy) w.DrawRect(Rect(max(fixed_width, rc.left), max(y, rc.top), sz.cx, sz.cy), SColorPaper);

		bool hasfocus = HasFocus() || holder.HasFocusDeep();

		for(i = max(firstRow, fixed_rows); i < total_rows; i++)
		{
			ItemRect& vi = vitems[i];
			if(vi.hidden) continue;

			bool even = coloring_mode == 2 ? (i - vi.n - fixed_rows) & 1 : false;

			for(j = max(firstCol, fixed_cols); j < total_cols; j++)
			{
				const ItemRect& hi = hitems[j];

				if(hi.hidden)
					continue;

				Item &it = GetItemSize(i, j, x, y, cx, cy, skip);
				if(skip)
					continue;

				if(y >= rc.bottom)
					goto end_paint;

				if(x >= rc.right)
					break;

				if(!w.IsPainting(0, y, sz.cx, cy))
					break;

				if(w.IsPainting(x, y, cx, cy))
				{
					bool iscur = draw_focus ? (i == curpos.y && j == curpos.x) : false;

					if(coloring_mode == 1)
						even = (j - hi.n - fixed_cols) & 1;

					int id = hi.id;

					dword style = en | (select_row ? vi.style : it.style) | hi.balign;
					dword istyle = it.style;

					if(hitems[j].wrap)
						style |= GD::WRAP;
					if(ctrlpos.y == i && edits[id].ctrl && edits[id].ctrl->IsShown())
						style |= GD::NOTEXT;
					if(it.ctrl)
						style |= GD::NOTEXT;

					if(coloring_mode > 0)
						style |= (even ? GD::EVEN : GD::ODD);
					if(hasfocus)
						style |= GD::FOCUS;
					
					if(IsValidCursor(anchor) && anchor != curpos && multi_select) { // mouse selection in progress
						Rect r(anchor, curpos);
						r.Normalize();
						r.SetSize(r.GetSize() + 1);
						if(select_row) {
							r.left = 0;
							r.right = INT_MAX;
						}
						if(r.Contains(Point(j, i)))
							style |= GD::SELECT;
					}

					Color cfg;
					Color cbg;

					Font fnt = StdFont();
					GridDisplay* gd;
					Value val = GetItemValue(it, id, hi, vi);
					GetItemAttrs(it, val, i, j, vi, hi, style, gd, cfg, cbg, fnt);

					Color fg = SColorText;
					Color bg = SColorPaper;

					bool custom = true;

					if(style & GD::CURSOR)
					{
						if(style & GD::FOCUS)
						{
							fg = iscur ? Blend(bg_focus, Black, 230) : fg_focus;
							bg = iscur ? Blend(bg_focus, White, 230) : bg_focus;
						}
						else
						{
							bg = Blend(SColorDisabled, bg);
						}
						custom = false;
					}
					else if(style & GD::LIVE)
					{
						fg = fg_live;
						bg = bg_live;
						custom = false;
					}
					else if(istyle & GD::FOUND)
					{
						fg = fg_found;
						bg = bg_found;
						custom = false;
					}
					else if(style & GD::SELECT)
					{
						fg = fg_select;
						bg = bg_select;
						custom = false;
					}
					else if(style & GD::EVEN)
					{
						fg = fg_even;
						bg = bg_even;
					}
					else if(style & GD::ODD)
					{
						fg = fg_odd;
						bg = bg_odd;
					}

					if(custom)
					{
						if(!IsNull(cfg)) fg = cfg;
						if(!IsNull(cbg)) bg = cbg;
					}
					
					if(style & GD::READONLY)
					{
						bg = Blend(bg, SGray(), 40);
						fg = Blend(fg, SGray(), 200);
					}

					gd->SetBgImage(vi.img);
					gd->col = j - fixed_rows;
					gd->row = i - fixed_cols;
					gd->parent = this;

					val = GetAttrTextVal(val);
					
					gd->Paint(w, x, y, cx, cy,
					          val, style,
					          fg, bg, fnt, it.style & GD::FOUND, it.fs, it.fe);
					
					it.rcx = gd->real_size.cx;
					it.rcy = gd->real_size.cy;

					if(vert_grid)
					{
						bool skip = false;
						if(!draw_last_vert_line && j == total_cols - 1)
							skip = true;

						if(!skip)
							w.DrawRect(x + cx - 1, y, 1, cy, fg_grid);
					}
					if(horz_grid)
					{
						bool skip = false;
						if(!draw_last_horz_line && i == total_rows - 1)
							skip = true;

						if(!skip)
							w.DrawRect(x, y + cy - 1, cx, 1, fg_grid);
					}

					if(iscur && draw_focus)
						Upp::DrawFocus(w, x, y, cx, cy);
				}
			}
		}

	end_paint:

		w.End();

		lastCol = j - 1;
		lastRow = i - 1;
	}


	if(moving_header && fixed_top_click && curSplitCol >= 0)
	{
		r.Set(fixed_width, 0, sz.cx, fixed_height);
		w.Clip(r);
		bool lastcol = curSplitCol == lastVisCol;

		int cp = curSplitCol;
		if(!lastcol)
			while(++cp < total_cols && hitems[cp].hidden);

		int cx = hitems[cp].nWidth() / 2;
		int x = lastcol ? hitems[curSplitCol].nLeft(sbx) + cx
		                : hitems[curSplitCol].nRight(sbx) - 1;
		DrawFatFrame(w, x, 0, cx, vitems[fixed_rows - 1].nBottom(), moving_allowed ? LtBlue : Red, 2);
		w.End();
	}

	if(moving_header && fixed_left_click)
	{
		int dy = curSplitRow == lastVisRow ? -2 : -1;
		int y = curSplitRow >= 0 ? vitems[curSplitRow].nBottom(sby) + dy : 0;
		if(y >= fixed_height - 1)
			DrawVertDragLine(w, y, hitems[fixed_cols - 1].nRight(), 0, moving_allowed ? LtBlue : Red);
	}

	if(moving_body)
	{
		int dy = curSplitRow == lastVisRow ? -2 : -1;
		int y = curSplitRow >= 0 ? vitems[curSplitRow].nBottom(sby) + dy : 0;
		if(y >= fixed_height - 1)
			DrawVertDragLine(w, y, sz.cx, fixed_width - 1, LtBlue);
	}

	if(search_display && !search_string.IsEmpty())
	{
		Size tsz = GetTextSize(search_string, StdFont());
		w.DrawRect(Rect(0, sz.cy - tsz.cy - 4, tsz.cx + 4, sz.cy), SRed);
		w.DrawText(2, sz.cy - tsz.cy - 2, search_string, StdFont(), SYellow);
	}

	if(!can_paint)
		w.DrawRect(Rect(0, total_cols > 1 ? fixed_height : 0, sz.cx, sz.cy), SColorPaper);
	if(++paint_flag > 100)
		paint_flag = 0;

	LLOG("---- Paint " << paintcnt);
}

};