#include "CtrlLib.h"

NAMESPACE_UPP

void CtrlsImageLook(Value *look, int i, int n)
{
	while(n--)
		*look++ = CtrlsImg::Get(i++);
}

void CtrlsImageLook(Value *look, int i, const Image& image, const Color *color, int n)
{
	for(int q = 0; q < n; q++)
		*look++ = ChLookWith(CtrlsImg::Get(i++), image, *color++);
}

void CtrlsImageLook(Value *look, int i, const Image& image, int n)
{
	for(int q = 0; q < n; q++)
		*look++ = ChLookWith(CtrlsImg::Get(i++), image);
}

CtrlsImgLook& CtrlsImgLook::operator()(int i, int n)
{
	while(n-- > 0)
		Add(CtrlsImg::Get(i++));
	return *this;
}

CtrlsImgLook& CtrlsImgLook::operator()(int i, const Image& img, Color (*fn)(int i), int n)
{
	for(int q = 0; q < n; q++)
		Add(ChLookWith(CtrlsImg::Get(i++), img, fn, q));
	return *this;
}

CtrlsImgLook& CtrlsImgLook::operator()(int i, const Image& img, int n)
{
	for(int q = 0; q < n; q++)
		Add(ChLookWith(CtrlsImg::Get(i++), img));
	return *this;
}

CtrlsImgLook::CtrlsImgLook(int i, int n)
{
	operator()(i, n);
}

CtrlsImgLook::CtrlsImgLook(int i, const Image& img, Color (*fn)(int i), int n)
{
	operator()(i, img, fn, n);
}

CtrlsImgLook::CtrlsImgLook(int i, const Image& img, int n)
{
	operator()(i, img, n);
}

String DeAmp(const char *s)
{
	String out;
	for(; *s; out.Cat(*s++))
		if(*s == '&')
			out.Cat('&');
	return out;
}

Size GetSmartTextSize(const char *text, Font font, int cx) {
	if(*text == '\1') {
		Size sz;
		RichText txt = ParseQTF(text + 1);
		txt.ApplyZoom(GetRichTextStdScreenZoom());
		sz.cx = min(cx, txt.GetWidth());
		sz.cy = txt.GetHeight(Zoom(1, 1), sz.cx);
		return sz;
	}
	return GetTLTextSize(ToUnicode(text, CHARSET_DEFAULT), font);
}

int GetSmartTextHeight(const char *s, int cx, Font font) {
	if(*s == '\1') {
		Size sz;
		RichText txt = ParseQTF(s + 1);
		txt.ApplyZoom(GetRichTextStdScreenZoom());
		return txt.GetHeight(Zoom(1, 1), cx);
	}
	int cy = font.Info().GetHeight();
	int h = cy;
	while(*s) {
		if(*s == '\n')
			h += cy;
		s++;
	}
	return h;
}

void DrawSmartText(Draw& draw, int x, int y, int cx, const char *text, Font font, Color ink, int accesskey, int accesspos) {
	if(*text == '\1') {
		Size sz;
		RichText txt = ParseQTF(text + 1, accesskey);
		txt.ApplyZoom(GetRichTextStdScreenZoom());
		txt.Paint(Zoom(1, 1), draw, x, y, cx);
		return;
	}
	DrawTLText(draw, x, y, cx, ToUnicode(text, CHARSET_DEFAULT), font, ink, accesskey, accesspos);
}

bool CompareAccessKey(byte accesskey, dword key)
{
	return accesskey && dword(ToUpper(accesskey) - 'A' + K_ALT_A) == key;
}

byte  ExtractAccessKey(const char *s, String& label, int *pos)
{
	byte accesskey = 0;
	if (pos != NULL)
		*pos = -1;
	String text;
	const char* start = s;
	bool qtf = *s == '\1';
	while(*s)
		if((*s == '&' && !qtf || *s == '\b') && s[1] && s[1] != '&') {
			accesskey = ToAscii(ToUpper(s[1]));
			if (pos != NULL)
				*pos = s - start; 
			s++;
		}
		else
			text.Cat(*s++);
	text.Shrink();
	label = text;
	return accesskey;
}

byte  ChooseAccessKey(const char *text, dword used)
{
	for(const char *s = text; *s; s++) {
		byte ac = ToAscii(*s);
		if(ac >= 'A' && ac <= 'Z' && (Ctrl::AccessKeyBit(ac) & used) == 0)
			return ac;
	}
	for(const char *s = text; *s; s++) {
		byte ac = ToUpper(ToAscii(*s));
		if(ac >= 'A' && ac <= 'Z' && ac != 'I' && ac != 'L' && (Ctrl::AccessKeyBit(ac) & used) == 0)
			return ac;
	}
	for(const char *s = text; *s; s++) {
		byte ac = ToUpper(ToAscii(*s));
		if(ac >= 'A' && ac <= 'Z' && (Ctrl::AccessKeyBit(ac) & used) == 0)
			return ac;
	}
	return 0;
}

DrawLabel::DrawLabel()
{
	push = focus = disabled = false;
	lspc = rspc = 0;
	ink = Null;
	align = valign = ALIGN_CENTER;
	accesskey = 0;
	accesspos = -1;
}

Size DrawLabel::GetSize(int txtcx) const
{
	return GetSize(txtcx, limg.GetSize(), lspc, rimg.GetSize(), rspc);
}

Size DrawLabel::GetSize(int txtcx, Size sz1, int lspc, Size sz2, int rspc) const
{
	Size isz(0, 0);
	Size txtsz = *text ? GetSmartTextSize(text, font, txtcx) : paintrect.GetStdSize();

	if(!IsNull(lspc)) {
		isz.cx = lspc;
		isz.cy = sz1.cy;
		isz.cx += sz1.cx;
	}

	if(!IsNull(rspc)) {
		isz.cx += rspc;
		if(sz2.cy > isz.cy) isz.cy = sz2.cy;
		isz.cx += sz2.cx;
	}

	isz.cy = max(txtsz.cy, max(sz1.cy, sz2.cy));
	isz.cx += txtsz.cx;

	return isz;
}

Image DisImage(const Image& m)
{
	Image mm = Grayscale(m, 200);
	ImageBuffer ib(mm);
	RGBA *s = ~ib;
	RGBA *e = s + ib.GetLength();
	while(s < e)
		(s++)->a /= 3;
	Premultiply(ib);
	return ib;
}

Image DisabledImage(const Image& img, bool dis)
{
	return dis ? MakeImage(img, GUI_GlobalStyle() == GUISTYLE_CLASSIC ? Etched : DisImage)
	           : img;
}

Size DrawLabel::Paint(Draw& w, const Rect& r, bool visibleaccesskey) const
{
	int lspc = this->lspc;
	int rspc = this->rspc;
	Size sz1 = limg.GetSize();
	Size sz2 = rimg.GetSize();
	int txtcx = r.GetWidth() - sz1.cx - Nvl(lspc, 0) - sz2.cx - Nvl(rspc, 0);
	Size txtsz = *text ? GetSmartTextSize(text, font, txtcx) : paintrect.GetStdSize();
	if(txtsz.cx) {
		if(txtsz.cx + sz1.cx + sz2.cx + Nvl(lspc, 0) + Nvl(rspc, 0) > r.GetWidth()) {
			sz2.cx = 0;
			rspc = 0;
		}
		if(txtsz.cx + sz1.cx + sz2.cx + Nvl(lspc, 0) + Nvl(rspc, 0) > r.GetWidth()) {
			sz1.cx = 0;
			lspc = 0;
		}
	}
	Size isz = GetSize(txtcx, sz1, lspc, sz2, rspc);
	Point p, ip;
	if(align == ALIGN_LEFT)
		p.x = r.left;
	else
	if(align == ALIGN_RIGHT)
		p.x = r.right - isz.cx;
	else
	if(align == ALIGN_CENTER)
		p.x = (r.right + r.left - isz.cx) / 2;
	if(valign == ALIGN_TOP)
		p.y = r.top;
	else
	if(valign == ALIGN_BOTTOM)
		p.y = r.bottom - isz.cy;
	else
	if(valign == ALIGN_CENTER)
		p.y = (r.bottom + r.top - txtsz.cy) / 2;
	Color color = ink;
	if(IsNull(color))
		color = disabled ? SColorDisabled : SColorLabel; /////////
	int ix;
	if(IsNull(lspc))
		ix = r.left + push;
	else {
		ix = p.x + push;
		p.x += sz1.cx;
		p.x += lspc;
	}
	int iy = push + (r.top + r.bottom - sz1.cy) / 2;

	if(sz1.cx)
		if(IsNull(lcolor))
			w.DrawImage(ix, iy, DisabledImage(limg, disabled));
		else
			w.DrawImage(ix, iy, limg, lcolor);

	iy = push + (r.top + r.bottom - sz2.cy) / 2;
	ix = (IsNull(rspc) ? r.right - sz2.cx : p.x + txtsz.cx + rspc) + push;
	if(sz2.cx)
		if(IsNull(rcolor))
			w.DrawImage(ix, iy, DisabledImage(rimg, disabled));
		else
			w.DrawImage(ix, iy, rimg, rcolor);
	paintrect.Paint(w, p.x + push, p.y + push, txtsz.cx, isz.cy, color, Null);
	if(*text) {
		if(disabled)
			DrawSmartText(w, p.x + push + 1, p.y + push + (isz.cy - txtsz.cy) / 2 + 1,
			              txtsz.cx, text, font, SColorPaper);
		DrawSmartText(w, p.x + push, p.y + push, txtcx,
		              text, font, color, visibleaccesskey ? accesskey : 0, visibleaccesskey ? accesspos : -1);
		if(focus)
			DrawFocus(w, p.x - 2, p.y, txtsz.cx + 5, isz.cy);
	}

	return isz;
}

Size DrawLabel::Paint(Draw& w, int x, int y, int cx, int cy, bool vak) const
{
	return Paint(w, RectC(x, y, cx, cy), vak);
}

void LabelBase::LabelUpdate() {}

LabelBase& LabelBase::SetLeftImage(const Image& img, int spc) {
	lbl.limg = img;
	lbl.lspc = spc;
	LabelUpdate();
	return *this;
}

LabelBase& LabelBase::SetRightImage(const Image& img, int spc) {
	lbl.rimg = img;
	lbl.rspc = spc;
	LabelUpdate();
	return *this;
}

LabelBase& LabelBase::SetPaintRect(const PaintRect& paintrect) {
	lbl.paintrect = paintrect;
	LabelUpdate();
	return *this;
}


LabelBase& LabelBase::SetText(const char *text) {
	lbl.text = text;
	LabelUpdate();
	return *this;
}

LabelBase& LabelBase::SetFont(Font font) {
	if(lbl.font != font) {
		lbl.font = font;
		LabelUpdate();
	}
	return *this;
}

LabelBase& LabelBase::SetInk(Color ink) {
	if(lbl.ink != ink) {
		lbl.ink = ink;
		LabelUpdate();
	}
	return *this;
}

LabelBase& LabelBase::SetAlign(int align) {
	if(lbl.align != align) {
		lbl.align = align;
		LabelUpdate();
	}
	return *this;
}

LabelBase& LabelBase::SetVAlign(int valign) {
	if(lbl.valign != valign) {
		lbl.valign = valign;
		LabelUpdate();
	}
	return *this;
}

Size LabelBase::PaintLabel(Draw& w, const Rect& r, bool disabled, bool push, bool focus, bool vak)
{
	DrawLabel lbl1 = lbl;
	lbl1.disabled = disabled;
	lbl1.push = push;
	lbl1.focus = focus;
	return lbl1.Paint(w, r, vak);
}


Size LabelBase::PaintLabel(Draw& w, int x, int y, int cx, int cy, bool disabled, bool push, bool focus, bool vak)
{
	return PaintLabel(w, RectC(x, y, cx, cy), disabled, push, focus, vak);
}

Size LabelBase::GetLabelSize() const
{
	return lbl.GetSize();
}

void LinkToolTipIn__();

LabelBase::~LabelBase() {
	LinkToolTipIn__();
}

void DrawFocus(Draw& w, int x, int y, int cx, int cy, Color c) {
	w.Clipoff(x, y, cx, cy);
	for(int a = 0; a < cx; a += CtrlImg::focus_h().GetWidth()) {
		w.DrawImage(a, 0, CtrlImg::focus_h(), c);
		w.DrawImage(a, cy - 1, CtrlImg::focus_h(), c);
	}
	for(int a = 0; a < cy; a += CtrlImg::focus_v().GetHeight()) {
		w.DrawImage(0, a, CtrlImg::focus_v(), c);
		w.DrawImage(cx - 1, a, CtrlImg::focus_v(), c);
	}
	w.End();
}

void DrawFocus(Draw& w, const Rect& r, Color c) {
	DrawFocus(w, r.left, r.top, r.Width(), r.Height(), c);
}

void DrawHorzDrop(Draw& w, int x, int y, int cx)
{
	w.DrawRect(x, y, cx, 2, SColorHighlight);
	w.DrawRect(x, y - 2, 1, 6, SColorHighlight);
	w.DrawRect(x + cx - 1, y - 2, 1, 6, SColorHighlight);
	w.DrawRect(x + 1, y - 1, 1, 4, SColorHighlight);
	w.DrawRect(x + cx - 2, y - 1, 1, 4, SColorHighlight);
}

void DrawVertDrop(Draw& w, int x, int y, int cy)
{
	w.DrawRect(x, y, 2, cy, SColorHighlight);
	w.DrawRect(x - 2, y, 6, 1, SColorHighlight);
	w.DrawRect(x - 2, y + cy - 1, 6, 1, SColorHighlight);
	w.DrawRect(x - 1, y + 1, 4, 1, SColorHighlight);
	w.DrawRect(x - 1, y + cy - 2, 4, 1, SColorHighlight);
}

Point GetDragScroll(Ctrl *ctrl, Point p, Size max)
{
	if(ctrl->IsReadOnly())
		return Point(0, 0);
	Size sz = ctrl->GetSize();
	Size sd = min(sz / 6, Size(16, 16));
	Point d(0, 0);
	if(p.x < sd.cx)
		d.x = p.x - sd.cx;
	if(p.x > sz.cx - sd.cx)
		d.x = p.x - sz.cx + sd.cx;
	if(p.y < sd.cy)
		d.y = p.y - sd.cy;
	if(p.y > sz.cy - sd.cy)
		d.y = p.y - sz.cy + sd.cy;
	d.x = minmax(d.x, -max.cx, max.cx);
	d.y = minmax(d.y, -max.cy, max.cy);
	return d;
}

Point GetDragScroll(Ctrl *ctrl, Point p, int max)
{
	return GetDragScroll(ctrl, p, Size(max, max));
}

Point DisplayPopup::Op(Point p)
{
	return p + GetScreenView().TopLeft() - ctrl->GetScreenView().TopLeft();
}

void DisplayPopup::LeftDown(Point p, dword flags)
{
	ctrl->LeftDown(Op(p), flags);
}

void DisplayPopup::LeftDrag(Point p, dword flags)
{
	Cancel();
	ctrl->LeftDrag(Op(p), flags);
}

void DisplayPopup::LeftDouble(Point p, dword flags)
{
	ctrl->LeftDouble(Op(p), flags);
}

void DisplayPopup::RightDown(Point p, dword flags)
{
	ctrl->RightDown(Op(p), flags);
}

void DisplayPopup::LeftUp(Point p, dword flags)
{
	ctrl->LeftUp(Op(p), flags);
}

void DisplayPopup::MouseWheel(Point p, int zdelta, dword flags)
{
	ctrl->MouseWheel(Op(p), zdelta, flags);
}

void DisplayPopup::MouseLeave()
{
	Cancel();
}

void DisplayPopup::MouseMove(Point p, dword flags)
{
	p += GetScreenView().TopLeft();
	if(!slim.Contains(p))
		MouseLeave();
}

void DisplayPopup::Paint(Draw& w)
{
	Rect r = GetSize();
	w.DrawRect(r, SColorPaper);
	display->PaintBackground(w, r, value, ink, paper, style);
	r.left += margin;
	display->Paint(w, r, value, ink, paper, style);
}

DisplayPopup::DisplayPopup()
{
	SetFrame(BlackFrame());
	display = NULL;
	paper = ink = Null;
	style = 0;
	item = slim = Null;
	margin = 0;
}

void DisplayPopup::Sync()
{
	Refresh();
	if(display && ctrl && !ctrl->IsDragAndDropTarget() && !IsDragAndDropTarget()) {
		Size sz = display->GetStdSize(value);
		if(sz.cx + 2 * margin > item.GetWidth() || sz.cy > item.GetHeight()) {
			Rect wa = GetWorkArea();
			slim = item + ctrl->GetScreenView().TopLeft();
			Rect r = item;
			r.right = max(r.right, r.left + sz.cx + 2 * margin);
			r.bottom = max(r.bottom, r.top + sz.cy);
			r.Inflate(1, 1);
			r.Offset(ctrl->GetScreenView().TopLeft());
			SetRect(r);
			if(!IsOpen())
				Ctrl::PopUp(ctrl, true, false, false);
			return;
		}
	}
	if(IsOpen())
		Close();
}

void DisplayPopup::Cancel()
{
	display = NULL;
	Sync();
}

bool DisplayPopup::IsOpen()
{
	return Ctrl::IsOpen();
}

bool DisplayPopup::HasMouse()
{
	return Ctrl::HasMouse() || ctrl && ctrl->HasMouse();
}

void DisplayPopup::Set(Ctrl *_ctrl, const Rect& _item,
                       const Value& _value, const Display *_display,
                       Color _ink, Color _paper, dword _style, int _margin)
{
	if(item != _item || ctrl != _ctrl || value != _value || display != _display || ink != _ink ||
	   paper != _paper || style != _style) {
		item = _item;
		ctrl = _ctrl;
		value = _value;
		display = _display;
		ink = _ink;
		paper = _paper;
		style = _style;
		margin = _margin;
		Sync();
	}
	else
		Refresh();
}

END_UPP_NAMESPACE
