#include "LayDes.h"

#define LLOG(x) // LOG(x)

struct ImlImageMaker : ImageMaker {
	String id;
	virtual String Key() const {
		return id;
	}
	virtual Image Make() const {
		Vector<String> s = Split(id, ':');
		if(s.GetCount() != 3)
			return LayImg::ImageError();
		Array<ImlImage> iml;
		int f;
		LoadIml(LoadFile(SourcePath(s[0], s[1])), iml, f);
		bool recolor = false;
		if(*s[2].Last() == '*') {
			s[2].Trim(s[2].GetLength() - 1);
			recolor = true;
		}
		Image m = LayImg::ImageError();
		for(int i = 0; i < iml.GetCount(); i++)
			if(iml[i].name == s[2]) {
				m = iml[i].image;
				break;
			}
		return recolor ? AdjustColors(m) : m;
	}
};

Image GetUscImage(const String& id)
{
	Image m = GetImlImage(id);
	if(!IsNull(m))
		return AdjustColors(m);
	ImlImageMaker q;
	q.id = id;
	return MakeImage(q);
}

EscValue EscColor(Color c)
{
	EscValue v;
	if(!IsNull(c)) {
		v.MapSet("r", c.GetR());
		v.MapSet("g", c.GetG());
		v.MapSet("b", c.GetB());
	}
	return v;
}

Color ColorEsc(EscValue v)
{
	return v.IsVoid() ? Color(Null) : Color(v.GetFieldInt("r"), v.GetFieldInt("g"), v.GetFieldInt("b"));
}

EscValue EscRect(const Rect& r)
{
	EscValue v;
	v.MapSet("left", r.left);
	v.MapSet("right", r.right);
	v.MapSet("top", r.top);
	v.MapSet("bottom", r.bottom);
	return v;
}

Rect RectEsc(EscValue v)
{
	return Rect(v.GetFieldInt("left"), v.GetFieldInt("top"),
	            v.GetFieldInt("right"), v.GetFieldInt("bottom"));
}

EscValue EscSize(Size sz)
{
	EscValue v;
	v.MapSet("cx", sz.cx);
	v.MapSet("cy", sz.cy);
	return v;
}

Size SizeEsc(EscValue v)
{
	return Size(v.GetFieldInt("cx"), v.GetFieldInt("cy"));
}

EscValue EscPoint(Point sz)
{
	EscValue v;
	v.MapSet("x", sz.x);
	v.MapSet("y", sz.y);
	return v;
}

Point PointEsc(EscValue v)
{
	return Point(v.GetFieldInt("x"), v.GetFieldInt("y"));
}

void SIC_Print(EscEscape& e)
{
	if(e[0].IsArray())
		VppLog() << (String) e[0];
	else
	if(e[0].IsNumber())
		VppLog() << e[0].GetNumber();
	else
	if(!e[0].IsVoid())
		e.ThrowError("invalid argument to 'print'");
}

bool GetOptFlag(EscEscape& e) {
	if(!e.GetCount())
		return true;
	return IsTrue(e[0]);
}

struct SIC_Font : public EscHandle {
	Font font;

	void Height(EscEscape& e) {
		e = EscFont(FontEsc(e.self).Height(e.Int(0)));
	}
	void Bold(EscEscape& e) {
		e = EscFont(FontEsc(e.self).Bold(GetOptFlag(e)));
	}
	void Italic(EscEscape& e) {
		e = EscFont(FontEsc(e.self).Italic(GetOptFlag(e)));
	}
	void Underline(EscEscape& e) {
		e = EscFont(FontEsc(e.self).Underline(GetOptFlag(e)));
	}

	typedef SIC_Font CLASSNAME;

	SIC_Font(EscValue& v) {
		v.Escape("Height(h)", this, THISBACK(Height));
		v.Escape("Bold(...)", this, THISBACK(Bold));
		v.Escape("Italic(...)", this, THISBACK(Italic));
		v.Escape("Underline(...)", this, THISBACK(Underline));
	}
};

EscValue EscFont(Font f)
{
	EscValue v;
	(new SIC_Font(v))->font = f;
	return v;
}

Font FontEsc(EscValue v)
{
	if(!v.IsMap())
		return Null;
	const VectorMap<EscValue, EscValue>& m = v.GetMap();
	int q = m.Find("Height");
	if(q < 0)
		return Null;
	const EscLambda& l = m[q].GetLambda();
	if(!dynamic_cast<SIC_Font *>(l.handle))
		return Null;
	Font f = ((SIC_Font *)l.handle)->font;
	if(f.GetHeight() == 0)
		f.Height(StdFont().GetHeight());
	return f;
}

void SIC_StdFont(EscEscape& e)
{
	if(e.GetCount() == 1)
		e = EscFont(StdFont()(e.Int(0)));
	else
		e = EscFont(StdFont());
}

void SIC_Arial(EscEscape& e)
{
	e = EscFont(Arial(e.Int(0)));
}

void SIC_Roman(EscEscape& e)
{
	e = EscFont(Roman(e.Int(0)));
}

void SIC_Courier(EscEscape& e)
{
	e = EscFont(Courier(e.Int(0)));
}

void SIC_GetImageSize(EscEscape& e)
{
	e.CheckArray(0);
	e = EscSize(GetUscImage((String)e[0]).GetSize());
}

void SIC_GetTextSize(EscEscape& e)
{
	if(e.GetCount() < 1 || e.GetCount() > 2)
		e.ThrowError("wrong number of arguments in call to 'GetTextSize'");
	e.CheckArray(0);
	WString text = e[0];
	Font font = StdFont();
	if(e.GetCount() > 1)
		font = FontEsc(e[1]);
	e = EscSize(GetTextSize(text, font));
}

void SIC_GetSmartTextSize(EscEscape& e)
{
	if(e.GetCount() < 1 || e.GetCount() > 2)
		e.ThrowError("wrong number of arguments in call to 'GetTextSize'");
	e.CheckArray(0);
	String text = ToUtf8((WString)(e[0]));
	ExtractAccessKey(text, text);
	Font font = StdFont();
	if(e.GetCount() > 1)
		font = FontEsc(e[1]);
	e = EscSize(GetSmartTextSize(text, font));
}

void SIC_GetQtfHeight(EscEscape& e)
{
	int zoom = e.Int(0);
	e.CheckArray(0);
	String text = e[1];
	int cx = e.Int(2);
	RichText doc;
	doc = ParseQTF(text);//!!!!!
	e = doc.GetHeight(Zoom(zoom, 1024), cx);
}

#ifdef _DEBUG
void SIC_DumpLocals(EscEscape& e)
{
	LOG("--- DUMP of SIC local variables -------------------------");
	for(int i = 0; i < e.esc.var.GetCount(); i++)
		LOG(e.esc.var.GetKey(i) << " = " << e.esc.var[i].ToString());
	LOG("---------------------------------------------------------");
}
#endif

static const char laysrc[] = {
"Color(r, g, b) { c.r = r; c.g = g; c.b = b; return c; }\n"
"Point(x, y) { p.x = x; p.y = y; return p; }\n"
"Size(cx, cy) { sz.cx = cx; sz.cy = cy; return sz; }\n"
"Rect(l, t, r, b) { e.left = l; e.top = t; e.right = r; e.bottom = b; return e; }\n"
"RectC(x, y, cx, cy) { e.left = x; e.top = y; e.right = x + cx; e.bottom = y + cy; return e; }\n"
};

void LayLib()
{
	ArrayMap<String, EscValue>& global = UscGlobal();

	Scan(global, laysrc, "laydes library");
	Escape(global, "StdFont(...)", SIC_StdFont);
	Escape(global, "Arial(h)", SIC_Arial);
	Escape(global, "Roman(h)", SIC_Roman);
	Escape(global, "Courier(h)", SIC_Courier);
	Escape(global, "GetImageSize(name)", SIC_GetImageSize);
	Escape(global, "GetTextSize(...)", SIC_GetTextSize);
	Escape(global, "GetSmartTextSize(...)", SIC_GetSmartTextSize);
	Escape(global, "GetQtfHeight(zoom, text, cx)", SIC_GetQtfHeight);
#ifdef _DEBUG
	Escape(global, "dump_locals()", SIC_DumpLocals);
#endif

	Escape(global, "print(x)", SIC_Print);

	global.Add("Black", EscColor(Black));
	global.Add("Gray", EscColor(Gray));
	global.Add("LtGray", EscColor(LtGray));
	global.Add("WhiteGray", EscColor(WhiteGray));
	global.Add("White", EscColor(White));
	global.Add("Red", EscColor(Red));
	global.Add("Green", EscColor(Green));
	global.Add("Brown", EscColor(Brown));
	global.Add("Blue", EscColor(Blue));
	global.Add("Magenta", EscColor(Magenta));
	global.Add("Cyan", EscColor(Cyan));
	global.Add("Yellow", EscColor(Yellow));
	global.Add("LtRed", EscColor(LtRed));
	global.Add("LtGreen", EscColor(LtGreen));
	global.Add("LtYellow", EscColor(LtYellow));
	global.Add("LtBlue", EscColor(LtBlue));
	global.Add("LtMagenta", EscColor(LtMagenta));
	global.Add("LtCyan", EscColor(LtCyan));
	global.Add("SBlack", EscColor(Black));
	global.Add("SGray", EscColor(Gray));
	global.Add("SLtGray", EscColor(LtGray));
	global.Add("SWhiteGray", EscColor(WhiteGray));
	global.Add("SWhite", EscColor(White));
	global.Add("SRed", EscColor(Red));
	global.Add("SGreen", EscColor(Green));
	global.Add("SBrown", EscColor(Brown));
	global.Add("SBlue", EscColor(Blue));
	global.Add("SMagenta", EscColor(Magenta));
	global.Add("SCyan", EscColor(Cyan));
	global.Add("SYellow", EscColor(Yellow));
	global.Add("SLtRed", EscColor(LtRed));
	global.Add("SLtGreen", EscColor(LtGreen));
	global.Add("SLtYellow", EscColor(LtYellow));
	global.Add("SLtBlue", EscColor(LtBlue));
	global.Add("SLtMagenta", EscColor(LtMagenta));
	global.Add("SLtCyan", EscColor(LtCyan));

	global.Add("IntNull", (int)Null);
	global.Add("DblNullLim", DOUBLE_NULL_LIM);
}

void EscDraw::DrawRect(EscEscape& e)
{
	if(e.GetCount() == 2)
		w.DrawRect(RectEsc(e[0]), ColorEsc(e[1]));
	else
	if(e.GetCount() == 5)
		w.DrawRect(e.Int(0), e.Int(1), e.Int(2), e.Int(3), ColorEsc(e[4]));
	else
		e.ThrowError("wrong number of arguments in call to 'DrawRect'");
}

void EscDraw::DrawLine(EscEscape& e)
{
	if(e.GetCount() == 4)
		w.DrawLine(PointEsc(e[0]), PointEsc(e[1]), e.Int(2), ColorEsc(e[3]));
	else
	if(e.GetCount() == 6)
		w.DrawLine(e.Int(0), e.Int(1), e.Int(2), e.Int(3), e.Int(4), ColorEsc(e[5]));
	else
		e.ThrowError("wrong number of arguments in call to 'DrawLine'");
}

void EscDraw::DrawText(EscEscape& e)
{
	if(e.GetCount() < 3 || e.GetCount() > 6)
		e.ThrowError("wrong number of arguments in call to 'DrawText'");
	int x = e.Int(0);
	int y = e.Int(1);
	Font font = StdFont();
	Color color = SColorText;
	if(e[2].IsInt())
	{
		int z = e.Int(2);
		e.CheckArray(3);
		WString text = e[3];
		if(e.GetCount() > 4)
			font = FontEsc(e[4]);
		if(e.GetCount() > 5)
			color = ColorEsc(e[5]);
		w.DrawText(x, y, z, text, Nvl(font, StdFont()), color);
	}
	else
	{
		e.CheckArray(2);
		WString text = e[2];
		if(e.GetCount() > 3)
			font = FontEsc(e[3]);
		if(e.GetCount() > 4)
			color = ColorEsc(e[4]);
		w.DrawText(x, y, text, Nvl(font, StdFont()), color);
	}
}

void EscDraw::DrawSmartText(EscEscape& e)
{
	if(e.GetCount() < 3 || e.GetCount() > 6)
		e.ThrowError("wrong number of arguments in call to 'DrawSmartText'");
	int x = e.Int(0);
	int y = e.Int(1);
	int ii = 2;
	int cx = INT_MAX;
	if(e[ii].IsInt())
		cx = e.Int(ii++);
	String text;
	if(ii < e.GetCount() && e[ii].IsArray())
		text = ToUtf8((WString)e[ii++]);
	byte accesskey = ExtractAccessKey(text, text);
	Font font = StdFont().Height(11);
	if(ii < e.GetCount())
		font = FontEsc(e[ii++]);
	if(font.GetHeight() == 0)
#ifdef PLATFORM_XFT
		font.Height(12);
#else
		font.Height(11);
#endif
	Color color = SColorText;
	if(ii < e.GetCount())
		color = ColorEsc(e[ii++]);
	::DrawSmartText(w, x, y, INT_MAX, text, font, color, accesskey);
}

void EscDraw::DrawQtf(EscEscape& e)
{
	if(e.GetCount() < 5 || e.GetCount() > 6)
		e.ThrowError("wrong number of arguments in call to 'DrawQtf'");
//	int zoom = e.Int(0);
	int x = e.Int(1);
	int y = e.Int(2);
	e.CheckArray(3);
	WString text = e[3];
	int cx = e.Int(4);
	String txt = '\1' + ToUtf8(text);
	::DrawSmartText(w, x, y, cx, txt, StdFont(), SColorText, 0);
}

void EscDraw::GetTextSize(EscEscape& e)
{
	if(e.GetCount() < 1 || e.GetCount() > 2)
		e.ThrowError("wrong number of arguments in call to 'GetTextSize'");
	e.CheckArray(0);
	WString text = e[0];
	Font font = StdFont();
	if(font.GetHeight() == 0)
#ifdef PLATFORM_XFT
		font.Height(12);
#else
		font.Height(11);
#endif
	if(e.GetCount() > 1)
		font = FontEsc(e[1]);
	e = EscSize(::GetTextSize(text, font));
}

void EscDraw::DrawImage(EscEscape& e)
{
	int cnt = e.GetCount();
	if(cnt == 2)
		w.DrawImage(RectEsc(e[0]), GetUscImage((String)e[1]));
	else
	if(cnt == 3)
		w.DrawImage(e.Int(0), e.Int(1), GetUscImage((String)e[2]));
	else
	if(cnt == 5)
		w.DrawImage(e.Int(0), e.Int(1), e.Int(2), e.Int(3), GetUscImage((String)e[4]));
	else
		e.ThrowError("wrong number of arguments in call to 'DrawImage'");
}

void EscDraw::DrawImageColor(EscEscape& e)
{
	int cnt = e.GetCount();
	if(cnt != 4 && cnt != 5 && cnt != 7)
		e.ThrowError("wrong number of arguments in call to 'DrawImageColor'");

	Image img = Colorize(GetUscImage((String)e[cnt - 3]), ColorEsc(e[cnt - 2]), e.Int(cnt - 1));

	if(cnt == 4)
		w.DrawImage(RectEsc(e[0]), img);
	else
	if(cnt == 5)
		w.DrawImage(e.Int(0), e.Int(1), img);
	else
	if(cnt == 7)
		w.DrawImage(e.Int(0), e.Int(1), e.Int(2), e.Int(3), img);
}

EscDraw::EscDraw(EscValue& v, Draw& w)
	: w(w)
{
	v.Escape("DrawRect(...)", this, THISBACK(DrawRect));
	v.Escape("DrawLine(...)", this, THISBACK(DrawLine));
	v.Escape("DrawText(...)", this, THISBACK(DrawText));
	v.Escape("DrawSmartText(...)", this, THISBACK(DrawSmartText));
	v.Escape("DrawQtf(...)", this, THISBACK(DrawQtf));
	v.Escape("GetTextSize(...)", this, THISBACK(GetTextSize));
	v.Escape("DrawImage(...)", this, THISBACK(DrawImage));
	v.Escape("DrawImageColor(...)", this, THISBACK(DrawImageColor));
}

ArrayMap<String, EscValue>& LayGlobal()
{
	static ArrayMap<String, EscValue> global;
	return global;
}
