#include "CtrlPos.h"

void CtrlPos::DrawAlignHandle(Draw& w, const Rect& _r, const Rect& r, const Ctrl::LogPos& pos, const Color& col)
{
	Point p;
	bool q;
	Size rz = r.GetSize();
	Color col2 = Blend(col, Black);

	p = r.CenterLeft();
	p.y -= rz.cy/4;
	q = (pos.x.GetAlign()&Ctrl::LEFT);
	w.DrawLine(p, Point(_r.left, p.y), (q)?(PEN_SOLID):(PEN_DOT), (q)?(col):(col2));

	p = r.CenterRight();
	p.y += rz.cy/4;
	q = (pos.x.GetAlign()&Ctrl::RIGHT);
	w.DrawLine(p, Point(_r.right, p.y), (q)?(PEN_SOLID):(PEN_DOT), (q)?(col):(col2));

	p = r.TopCenter();
	p.x -= rz.cx/4;
	q = (pos.y.GetAlign()&Ctrl::TOP);
	w.DrawLine(p, Point(p.x, _r.top), (q)?(PEN_SOLID):(PEN_DOT), (q)?(col):(col2));

	p = r.BottomCenter();
	p.x += rz.cx/4;
	q = (pos.y.GetAlign()&Ctrl::BOTTOM);
	w.DrawLine(p, Point(p.x, _r.bottom), (q)?(PEN_SOLID):(PEN_DOT), (q)?(col):(col2));
}

bool CtrlPos::GetAlignMode(const Rect& _r, const Rect& r, const Point& pp, Ctrl::LogPos& pos, int handsize)
{
	int size = handsize+2;
	Rect c;
	Point p;
	int q;
	Size rz = r.GetSize();
	
	q = pos.x.GetAlign();
	
	p = r.CenterLeft();
	p.y -= rz.cy/4;
	c = Rect(p, Point(_r.left, p.y)); c.InflateVert(size); c.Normalize();
	if(c.Contains(pp)) { q&= ~Ctrl::LEFT; q|= (pos.x.GetAlign()&Ctrl::LEFT)?(0):(Ctrl::LEFT); pos.x.SetAlign(q); return true; }

	p = r.CenterRight();
	p.y += rz.cy/4;
	c = Rect(p, Point(_r.right, p.y)); c.InflateVert(size); c.Normalize();
	if(c.Contains(pp)) { q&= ~Ctrl::RIGHT; q|= (pos.x.GetAlign()&Ctrl::RIGHT)?(0):(Ctrl::RIGHT); pos.x.SetAlign(q); return true; }

	q = pos.y.GetAlign();
	
	p = r.TopCenter();
	p.x -= rz.cx/4;
	c = Rect(p, Point(p.x, _r.top)); c.InflateHorz(size); c.Normalize();
	if(c.Contains(pp)) { q&= ~Ctrl::TOP; q|= (pos.y.GetAlign()&Ctrl::TOP)?(0):(Ctrl::TOP); pos.y.SetAlign(q); return true; }

	p = r.BottomCenter();
	p.x += rz.cx/4;
	c = Rect(p, Point(p.x, _r.bottom)); c.InflateHorz(size); c.Normalize();
	if(c.Contains(pp)) { q&= ~Ctrl::BOTTOM; q|= (pos.y.GetAlign()&Ctrl::BOTTOM)?(0):(Ctrl::BOTTOM); pos.y.SetAlign(q); return true; }
	
	return false;
}

void CtrlPos::DrawHintFrame(Draw& w, const Ctrl& g, const Ctrl& q, const Color& hintcol)
{
	Ctrl* c = q.GetFirstChild();
	while(c)
	{
		Ctrl* e(c);
		Point p; p.Clear();
		int f(flags);
		filter(e, p, f);
		if(e && c->InView())
		{
			Rect r = c->GetRect();
			if(c->InView())
				r.Offset(c->GetParent()->GetView().TopLeft());
			r.Offset(CtrlMover::GetOffset(*(c->GetParent()), g));
			
			r.Inflate(1);
			RectCtrl::DrawHandleFrame(w, r, hintcol, 1);
		}
		if(f & CtrlFinder::DEEP)
			DrawHintFrame(w, g, *c, hintcol);
		c = c->GetNext();	
	}
}

void CtrlPos::Paint(Draw& w)
{
	Size sz = GetSize();
	
	if(!IsTransparent())
		w.DrawRect(0,0,sz.cx,sz.cy, SColorFace());

	if(!IsEnabled()) return;	

	if(pctrl)
		DrawHintFrame(w, *pctrl, *pctrl, LtGray());
	
	if(!ctrl) return;
	Ctrl& c = *ctrl;
	if(&c == pctrl) return;

	Rect r = c.GetRect();
	Rect _r = c.GetParent()->GetView();
	Rect tr(r);

	if(c.InView())
		r.Offset(_r.TopLeft());
	Point op = CtrlMover::GetOffset(*c.GetParent(), *pctrl);
	r.Offset(op);
	_r.Offset(op);

	RectCtrl::DrawHandleFrame(w, r, style->framecol, style->framesize);

	DrawAlignHandle(w, _r, r, c.GetPos(), style->framecol);

	RectCtrl::DrawHandle(w, r, style->handcol, style->handsize);

	if(pressed)// && moving)
		RectCtrl::DrawRectInfo(w, Point(10,10), tr, style->framecol, style->textcol);
}

void CtrlPos::LeftDown(Point p, dword keyflags)
{
	if(IsReadOnly() || !IsEnabled()) return;

	SetCapture();
	moving = false;
	pressed = (keyflags & K_MOUSELEFT);

	if(ctrl)
	{
		Ctrl& c = *ctrl;
		//if already found prepare moving
		ASSERT(!c.InFrame());
		xpos = c.GetPos();
		xp = p;
	
		Rect r = c.GetRect();
		Rect _r = c.GetParent()->GetView();

		if(c.InView())
			r.Offset(_r.TopLeft());
		Point op = CtrlMover::GetOffset(*c.GetParent(), *pctrl);
		r.Offset(op);
		_r.Offset(op);

		Ctrl::LogPos pos = xpos;
		Rect rr(r); rr.Inflate(style->handsize+2);

		if(GetAlignMode(_r, rr, p, pos, style->handsize))
		{
			pos = LogPosPopUp::MakeLogPos(pos, c);		
			c.SetPos(pos);
			Action();
			Refresh();
			return;
		}
		else if((mode = RectCtrl::GetMode(r, p, keyflags, style->handsize)) != RectCtrl::NONE)
		{
			ci = RectCtrl::SetCursor(mode, keyflags, ci);
			Refresh();
			return;
		}
	}

	if(pctrl)
	{
		Point pt(p);
		int f(flags);
		ctrl = CtrlFinder::GetCtrl(*pctrl, pt, f, filter);
		if(ctrl)
		{
			if(ctrl->InFrame()) //may not move base nor frames
				ClearCtrl();
			else
				WhenLeftDown(*ctrl, p, keyflags);
		}
		Action();
	}

	Refresh();
}

void CtrlPos::MouseMove(Point p, dword keyflags)
{
	if(IsReadOnly() || !IsEnabled()) return;

	moving = true;
	pressed = (keyflags & K_MOUSELEFT);
	//int m = RectCtrl::GetMode(r, p, keyflags, style->handsize);
	//ci = RectCtrl::SetCursor(m, keyflags);
	if(!ctrl) return;
	Ctrl& c = *ctrl;

	if(pressed && mode != RectCtrl::NONE) 
	{
		if(keyflags & K_ALT)
		{
			Ctrl* q = c.GetParent();
			Ctrl* prevc = c.GetPrev();

			//calculate some things not accessible when Removed()

			Rect r = LogPosPopUp::CtrlRect(xpos, q->GetSize());
			if(c.InView())
				r.Offset(c.GetParent()->GetView().TopLeft());
			Point ops = CtrlMover::GetOffset(*(c.GetParent()), *pctrl);
			r.Offset(ops);

			c.Remove(); //prevent moving control from finding when searching new parent
			Point pt(p);
			int ft(flags); flags |= (DEEP | NEST);
			Ctrl* pc = GetCtrl(*pctrl, pt, flags, filter);
			flags &= ~(DEEP | NEST); flags |= (ft & DEEP); //restore DEEP flag from save, NEST is ours
			if(!pc) pc = pctrl;
			if(pc != q)
			{
				r.Offset(-pc->GetView().TopLeft());
				Point opd = CtrlMover::GetOffset(*pc, *pctrl);
				r.Offset(-opd);

				xpos = LogPosPopUp::MakeLogPos(xpos, r, pc->GetSize());
				
				pc->Add(c);
			}
			else
				q->AddChild(&c, prevc); //undo Remove();
		}

		Size psz = c.GetParent()->GetSize();
		Rect r = LogPosPopUp::CtrlRect(xpos, psz);
		RectCtrl::CalcRect(r, p-xp, keyflags, mode, g);
		r.Normalize();
		Ctrl::LogPos pos = LogPosPopUp::MakeLogPos(xpos, r, psz);
		GetCtrl()->SetPos(pos);
		Action();
		Refresh();
	}
}

void CtrlPos::LeftUp(Point p, dword keyflags)
{
	ReleaseCapture();
	pressed = false;
	moving = false;
	//xpos.SetNull();
	xp.SetNull();
	mode = RectCtrl::NONE;
	if(IsReadOnly() || !IsEnabled()) return;

	ci = RectCtrl::SetCursor(mode, keyflags, ci);
	Refresh();
}

void CtrlPos::RightDown(Point p, dword keyflags)
{
	//cancel
	if(!(keyflags & K_SHIFT))
		LeftDown(p, keyflags);
	ReleaseCapture();
	int m = mode;
	mode = RectCtrl::NONE;
	if(IsReadOnly() || !IsEnabled()) return;

	ci = RectCtrl::SetCursor(mode, keyflags, ci);
	if(!GetCtrl()) return;

	if(pressed)
	{
		if(m != RectCtrl::NONE)
		{
			GetCtrl()->SetPos(xpos);
			Action();
			Refresh();
		}
	}
	else if(keyflags & K_SHIFT)
	{
		pressed = false;
		moving = false;
		//xpos.SetNull();
		xp.SetNull();
		//mode = RectCtrl::NONE;
		RectTracker tr(*this);
		Size psz = GetCtrl()->GetParent()->GetSize();
		Rect r = tr.Track(Rect(p,p));

		Ctrl* pc = GetCtrl()->GetParent();
		r.Offset(-pc->GetView().TopLeft());
		Point opd = CtrlMover::GetOffset(*pc, *pctrl);
		r.Offset(-opd);

		Ctrl::LogPos pos = LogPosPopUp::MakeLogPos(xpos, r, psz);
		GetCtrl()->SetPos(pos);
		Action();
		Refresh();
	}
	else if(WhenBar)
		MenuBar::Execute(WhenBar);
}

void CtrlPos::MouseWheel(Point p, int zdelta, dword keyflags)
{
	//if(!IsEditable()) return;
	//if(!HasFocus()) SetFocus();
	if(!GetCtrl()) return;
	Ctrl& c = *GetCtrl();
	
	int i = zdelta/120;

	for(; i<0; ++i)
	{
		Ctrl* p = c.GetNext();
		c.GetParent()->AddChild(&c, p);
		c.Refresh();
	}
	for(; i>0; --i)
	{
		Ctrl* p = c.GetPrev();
		c.GetParent()->AddChildBefore(&c, p);
		c.Refresh();
	}
	
}

void CtrlPos::LeftDouble(Point p, dword flags)
{
	//LeftDown already called
	WhenLeftDouble();
}

bool CtrlPos::Key(dword key, int count)
{
	if(key == K_SPACE)
	{
		ClearCtrl();
		Action();
		Refresh();
		return true;	
	}
	return false;
}

void CtrlPos::Updated()
{
	//refresh the view of the currently selected ctrl
	CtrlFinder::Updated();
}

CtrlPos::CtrlPos()
	: pressed(false), moving(false), mode(RectCtrl::NONE), g(4,4)
{
	BackPaint();
	NoIgnoreMouse();
	style = &RectCtrl::StyleDefault();
	//xpos.SetNull();
	xp.SetNull();
}
