#include "GridCtrl.h"

namespace Upp {

#define LLOG(x) // DLOG(x)
	
int GridCtrl::GetMouseCol(Point &p, bool relative, bool fixed, bool full)
{
	if(!full && p.x < fixed_width)
		return -1;

	int dx = 0;

	if(relative)
		dx += sbx;

	int first_col = fixed ? 0 : max(firstVisCol, fixed_cols);
	int last_col = max(lastVisCol, fixed_cols - 1);

	if(!fixed && last_col >= total_cols)
		return -1;

	for(int i = first_col; i <= last_col; i++)
	{
		if(p.x >= hitems[i].nLeft(dx) &&
		   p.x  < hitems[i].nRight(dx))
			return i;
	}
	return -1;
}

int GridCtrl::GetMouseRow(Point &p, bool relative, bool fixed, bool full)
{
	if(!full && p.y < fixed_height)
		return -1;

	int dy = 0;

	if(relative)
		dy += sby;

	int first_row = fixed ? 0 : max(firstVisRow, fixed_rows);
	int last_row = max(lastVisRow, fixed_rows - 1);
	
	if(!fixed && last_row >= total_rows)
		return -1;

	for(int i = first_row; i <= last_row; i++)
	{
		if(p.y >= vitems[i].nTop(dy) &&
		   p.y  < vitems[i].nBottom(dy))
			return i;
	}
	return -1;
}

Point GridCtrl::GetPos(Point p)
{
	return Point(GetMouseCol(p, true, false), GetMouseRow(p, true, false));
}

void GridCtrl::LeftDown(Point p, dword keyflags)
{
	//popup.Close();
	SetCapture();
	leftpnt = p;
	just_clicked = true;

	fixed_top_click  = p.x >= fixed_width && p.y < fixed_height;
	fixed_left_click = p.x < fixed_width && p.y >= fixed_height;
	fixed_click      = fixed_top_click || fixed_left_click;
	top_click        = p.y < fixed_height;

	resizing = curResizeCol || curResizeRow;

	if(resizing)
	{
		popup.Close();
		
		splitCol  = curSplitCol;
		splitRow  = curSplitRow;
		resizeCol = curResizeCol;
		resizeRow = curResizeRow;

		Split(GS_DOWN);
		return;
	}
	else if(fixed_click)
	{
		moveCol = oldMoveCol = GetMouseCol(p, fixed_top_click, 1);
		moveRow = oldMoveRow = GetMouseRow(p, fixed_left_click, 1);
		return;
	}

	SetFocus();

	if(IsEmpty() || IsReadOnly())
		return;

	bool is_shift = keyflags & K_SHIFT;
	bool is_ctrl = keyflags & K_CTRL;
	
	Point oldcur = curpos;

	CurState cs = SetCursor0(p, CU_MOUSE | CU_HIDECTRLS);
	bool state_change = cs.IsValid() && !cs.IsNew() && (is_ctrl || is_shift);

	if(!cs.IsAccepted() && !state_change)
		return;

	anchor = curpos;

	if(cs || state_change) {
		moveCol = curpos.x;
		moveRow = curpos.y;

		if((keyflags & K_CTRL) && multi_select) {
			SelectRange(curpos, curpos, !IsSelect(curpos.y, curpos.x, false), select_row);
		}
		else {
			ClearSelection();
			if((keyflags & K_SHIFT) && multi_select && IsValidCursor(oldcur)) {
				if(oldcur.y >= 0 && oldcur.x >= 0)
					SetCursor0(oldcur, CU_HIDECTRLS);
				Refresh();
			}
		}
	}

	#ifdef LOG_CALLBACKS
	//LGR(2, "WhenLeftClick()");
	#endif

	WhenLeftClick();

	if(editing && one_click_edit && cs.IsValid() ) //&& IsRowEditable() ?
		StartEdit();
	else
		RebuildToolBar();
	
	SetCapture();
}

void GridCtrl::MouseMove(Point p, dword keyflags)
{
	mouse_move = true;

	if(resizing)
	{
		int si, lp, mp, off;
		RectItems *its;
		static int sub = 0;

		if(resizeCol)
		{
			mp = p.x;
			lp = leftpnt.x;
			si = splitCol;
			its = &hitems;
			bool top = fixed_top_click || (!fixed_left_click && full_col_resizing);
			off = top ? sbx : 0;
		}
		else
		{
			mp = p.y;
			lp = leftpnt.y;
			si = splitRow;
			its = &vitems;
			bool left = fixed_left_click || (!fixed_top_click && full_row_resizing);
			off = left ? sby : 0;
		}

		int right = (*its)[si].nRight(off);

		if(just_clicked)
		{
			sub = right - lp;
			just_clicked = false;
		}

		if(SetDiffItemSize(resizeCol, *its, si, mp - right + sub))
		{
			Split(GS_MOVE);
		}

		return;
	}
	else if(fixed_click)
	{
		if((fixed_top_click && !moving_cols) ||
		   (fixed_left_click && !moving_rows) ||
		   moveCol < 0 || moveRow < 0)
		   return;

		if(!moving_header)
		{
			int diffx = p.x - leftpnt.x;
			int diffy = p.y - leftpnt.y;
			if(abs(diffx) < 5 && abs(diffy) < 5)
				return;

			p -= Point(diffx, diffy);

			moving_header = true;
			int idx = hitems[moveCol].id;
			int idy = vitems[moveRow].id;
			pophdr.val = idy > 0 ? GetConvertedColumn(moveCol, items[idy][idx].val) : items[idy][idx].val;

			if(fixed_top_click)
			{
				pophdr.sortmode = hitems[moveCol].sortmode;
				pophdr.sortcol = hitems[moveCol].sortcol;
				pophdr.sortcnt = sortOrder.GetCount();

				UpdateHighlighting(GS_POPUP, p);
			}
			else
			{
				pophdr.sortmode = 0;
				pophdr.sortcol = -1;
				pophdr.sortcnt = 0;
			}

			dx = hitems[moveCol].nLeft(fixed_top_click ? sbx : 0) - p.x;
			dy = vitems[moveRow].nTop(fixed_left_click ? sby : 0) - p.y;
		}


		Point pt = p + GetScreenRect().TopLeft() + GetBarOffset();

		pophdr.display = display;
		pophdr.chameleon = chameleon;
		pophdr.PopUp(this, pt.x + dx, pt.y + dy, hitems[moveCol].nWidth(), vitems[moveRow].nHeight());

		if(fixed_top_click && curSplitCol != oldMoveCol)
		{
			moving_allowed = CanMoveCol(moveCol, curSplitCol);
			RefreshTop();
			//Refresh(oldMoveCol >= 0 ? hitems[oldMoveCol].nRight(sbx) : 0, 0, hitems[curSplitCol].nRight(sbx), fixed_height);
			oldMoveCol = curSplitCol;
		}

		if(fixed_left_click && curSplitRow != oldMoveRow)
		{
			Refresh(0, 0, fixed_width, fixed_height);
			RefreshLeft();
			oldMoveRow = curSplitRow;
		}
		return;
	}

	if(leftpnt != p && p.y < fixed_height)
	{
		UpdateHighlighting(GS_MOVE, p);
	}

	if(live_cursor && popup.IsOpen())
	{
		LLOG("MouseMove:LiveCursor");
		if(IsMouseBody(p))
			SetCursor0(p, CU_MOUSE | CU_HIGHLIGHT);
		else
			SetCursor0(-1, -1, CU_HIGHLIGHT);
	}
	
	if(HasCapture())
	{
		if(!moving_body)
		{
			anchor = GetPos(p);
			Refresh();
			return;
	/*		if(keyflags & K_SHIFT)
			{
				if(SetCursor0(p, CU_MOUSE))
					DoShiftSelect();
				return;
			}

			bool select = true;
			if(select_row && !multi_select)
				select = false;

			if(select && (keyflags & K_CTRL))
			{
				if(SetCursor0(p, CU_MOUSE))
//					DoCtrlSelect();
				return;
			}*/
		}

		if(moveCol < 0 || moveRow < 0)
			return;

		if(!dragging)
			return;
		
		if(!moving_body)
		{
			popup.Close();
			
			if(!top_click && valid_cursor &&
			   p.x < total_width &&
			   p.y < total_height &&
			   (abs(p.y - leftpnt.y) > 5 ||
			    abs(p.x - leftpnt.x) > 5))
				moving_body = true;

			oldMoveRow = -1;
		}
		else
		{
			Point pt = p + GetScreenRect().TopLeft();

			int row = curSplitRow - fixed_rows + 2;
			if(select_row)
			{
				int count = max(1, selected_rows);

				if(vitems[curpos.y].IsSelect())
					popup.val = Format(t_("Moving selection (%d %s) before row %d"), count, count == 1 ? t_("row") : t_("rows"), row);
				else
					popup.val = Format(t_("Moving row %d before row %d"), curpos.y - fixed_rows + 1, row);
			}
			else
			{
				int count = max(1, selected_items);
				popup.val = Format(t_("Moving %d %s before row %d"), count, count == 1 ? t_("cell") : t_("cells"), row);
			}

			int px = pt.x + 15;
			int py = pt.y + GD_ROW_HEIGHT;

			popup.gd = display;
			popup.gd->row = 0;
			popup.gd->col = 0;
			popup.fg = SColorText;
			popup.bg = SColorPaper;
			popup.fnt = StdFont();
			popup.style = 0;
			popup.PopUp(this, px, py, GetTextSize((String) popup.val, StdFont()).cx + 10, GD_ROW_HEIGHT);
			SetFocus();

			if(curSplitRow != oldMoveRow || scrollLeftRight)
			{
				int dy = sby;
				if(oldMoveRow >= 0)
					Refresh(Rect(0, vitems[oldMoveRow].nBottom(dy) - 5, GetSize().cx, vitems[oldMoveRow].nBottom(dy) + 5));
				else
					Refresh(Rect(0, 0, GetSize().cx, 5));
				if(curSplitRow >= 0)
					Refresh(Rect(0, vitems[curSplitRow].nBottom(dy) - 5, GetSize().cx, vitems[curSplitRow].nBottom(dy) + 5));
				else
					Refresh(Rect(0, 0, GetSize().cx, 5));

				oldMoveRow = curSplitRow;
				popup.Refresh();

				scrollLeftRight = false;
			}
		}
	}
	else
		SyncPopup();
}

void GridCtrl::LeftUp(Point p, dword keyflags)
{
	LLOG("LeftUp");

	ReleaseCapture();
	Refresh();

	fixed_click = false;

	UpdateHighlighting(resizing ? GS_MOVE : GS_UP, p);

	if(moving_header)
	{
		LLOG("moving_header");
		pophdr.Close();

		moving_header = false;
		if(fixed_top_click)
			MoveCol(moveCol, curSplitCol);
		else
			MoveRow(moveRow, curSplitRow);

		if(focused_ctrl)
			focused_ctrl->SetFocus();

		fixed_top_click = false;
		fixed_left_click = false;

		return;
	}

	if(resizing)
	{
		Split(GS_UP);
		resizeCol = resizeRow = resizing = false;
		return;
	}

	if(fixed_top_click && sorting && Distance(leftpnt, p) < 3)
	{
		int i = GetMouseRow(leftpnt, false, true);
		int j = GetMouseCol(leftpnt, true, false);

		if(j >= fixed_cols && i == 0 && hitems[i].sortable)
		{
			int newSortCol = hitems[j].id;

			if(sorting_multicol && (keyflags & K_CTRL))
			{
				int colidx = InMultisort(newSortCol);

				if(colidx < 0)
				    sortOrder.Add(newSortCol);

				int cnt = sortOrder.GetCount();

				hitems[j].ChangeSortMode(newSortCol == sortOrder[cnt - 1]);

				if(colidx >= 0)
				{
					if(hitems[j].sortmode == 0)
					{
						sortOrder.Remove(colidx);
						cnt--;
					}
					
					if(WhenSort)
						WhenSort();
					else
					{
						if(hitems[j].sortmode > 0 && colidx == cnt - 1)
							GSort();
						else
							Multisort();
					}
				}
				else
				{
					hitems[j].sortcol = cnt;
					if(WhenSort)
						WhenSort();
					else
						GSort();
				}
			}
			else
			{
				if(sortCol >= 0 && sortCol != newSortCol)
				{
					int idx = GetIdCol(sortCol, true);
					hitems[idx].sortmode = 0;
				}

				ClearMultisort(1);
				hitems[j].ChangeSortMode();
				hitems[j].sortcol = 1;

				if(hitems[j].sortmode == 0)
					sortCol = -1;
				else
					sortCol = newSortCol;

				sortOrder.Add(newSortCol);
				
				if(WhenSort)
					WhenSort();
				else
					GSort(newSortCol, hitems[j].sortmode, fixed_rows);
			}

			UpdateCursor();
			Repaint(false, true);
			
			if(WhenSorted)
				WhenSorted();
		}
    }

	if(moving_body)
	{
		popup.Close();
		moving_body = false;
		MoveRows(curSplitRow + 1, !vitems[curpos.y].IsSelect());
		return;
	}
	
	if(IsValidCursor(anchor) && IsValidCursor(curpos) && multi_select)
		SelectRange(Rect(anchor, curpos), true, select_row);
	
	anchor = curpos;
}

void GridCtrl::LeftDouble(Point p, dword keyflags)
{
	LLOG("LeftDouble");

	if(full_col_resizing && curSplitCol >= 0)
		return;

	if(full_row_resizing && curSplitRow >= 0)
		return;

	if(IsEmpty() || !IsMouseBody(p) || IsReadOnly())
		return;

	if(keyflags & K_SHIFT || keyflags & K_CTRL)
		return;

	if(!valid_cursor)
		return;

	if(editing)
		StartEdit();

	if(!IsCtrl(curpos))
	{
		popup.Close();
		#ifdef LOG_CALLBACKS
		LGR(2, "WhenLeftDouble()");
		#endif
		WhenLeftDouble();
	}
}

void GridCtrl::LeftRepeat(Point p, dword keyflags)
{
	if(!moving_header && !resizeCol && !resizeRow)
		MouseAccel(p, resize_col_mode == 0, resize_row_mode == 0, keyflags);
}

void GridCtrl::RightDown(Point p, dword keyflags)
{
	if(IsReadOnly())
		return;

	if(total_rows > fixed_rows)
	{
		if(!EndEdit())
			return;

		SetCursor0(p, CU_MOUSE);
	}

	RebuildToolBar();
	SetFocus(); //jak nie bedzie menu to fokous zostanie na danym wierszu
	MenuBar::Execute(WhenMenuBar);
	anchor = curpos;
}

void GridCtrl::MouseLeave()
{
	if(live_cursor)
	{
		LLOG("MouseLeave:LiveCursor");
		SetCursor0(-1, -1, CU_HIGHLIGHT);
	}
	UpdateHighlighting(GS_BORDER, Point(0, 0));
	oldSplitCol = -1;
	oldSplitRow = -1;
	//popup.Close();
}

void GridCtrl::MouseWheel(Point p, int zdelta, dword keyflags)
{
	if(resize_row_mode == 0)
	{
		sby.Set(sby - zdelta / 4);
	}
}

};