#include "SvgDraw.h"

#include <CtrlLib/CtrlLib.h> //TODO: remove
#include <Web/Web.h>

NAMESPACE_UPP

void SvgDraw::Init(int xPixels, int yPixels)
{
	_nextId = 1;
	
	head.Tag("svg")
		("xmlns", "http://www.w3.org/2000/svg")
		("xmlns:xlink", "http://www.w3.org/1999/xlink")
		("width", xPixels)
		("height", yPixels)
		("viewBox", String().Cat() << "0 0 " << xPixels << " " << yPixels);
	inop = false;
	indent = "\t";
}

void SvgDraw::AddTag(XmlTag &tag)
{ 
	body << indent << tag(); 
}

void SvgDraw::AddTag(XmlTag &tag, const String &text)   
{ 
	body << indent << tag(text); 
}

String SvgDraw::AddPath(const Rect& r)
{
	String id = GetNextId();
	XmlTag path("path");
	
	StringBuffer p;
	p << "M" << r.left << " " << r.top << 
			" h" << r.Width() << " v" << r.Height() << " h-" << r.Width() << " Z";
			
	path("d", p);

	defs << "\t\t<clipPath id=\"" << id << "\">\n\t\t\t" << path() << "\t\t</clipPath>\n" ;
	return id;
}

void SvgDraw::WriteOp()	
{ 
	body << indent << op << ">\n"; 
}

void SvgDraw::AddOp(const char *attr, const char *val)
{
	op << ' ' << attr << "=\"" << val << "\"";
}

String SvgDraw::Finish()
{
	ASSERT(!inop);
	StringBuffer final;
	final << XmlHeader() << XmlDocType("svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\"")
		  << head() << "\t<defs>\n" << ~defs << "\t</defs>\n" << ~body << "</svg>";
	return ~final;
}

void SvgDraw::BeginOp()
{
	op = "<g";
	inop = true;
}

void SvgDraw::EndOp()
{
	IndentLess();
	body << indent << "</g>\n";	
	inop = false;
}

void SvgDraw::OffsetOp(Point p)
{
	Begin();
	AddOp("transform", String("translate(") << p.x << ", " << p.y << ")");
	WriteOp();
	IndentMore();
	actual_offset += p;
}

bool SvgDraw::ClipOp(const Rect& r)
{
	Begin();
	AddOp("clip-path", String("url(#") << AddPath(r) << ")");
	WriteOp();
	IndentMore();
	return false; // todo:
}

bool SvgDraw::ClipoffOp(const Rect& r)
{
	Begin();
	AddOp("transform", String("translate(") << r.left << ", " << r.top << ")");
	AddOp("clip-path", String("url(#") << AddPath(r.Offseted(-r.TopLeft())) << ")");
	WriteOp();
	IndentMore();
	actual_offset += r.TopLeft();
	return false; // todo:
}

bool SvgDraw::ExcludeClipOp(const Rect& r)
{
	//todo:
	PromptOK(String().Cat() << "ExcludeClipOp(" << r << ") not implemented");
	return false;
}

bool SvgDraw::IntersectClipOp(const Rect& r)
{
	//todo:
	PromptOK(String().Cat() << "IntersectClipOp(" << r << ") not implemented");
	return false;
}

Rect SvgDraw::GetClipOp() const
{
	//todo:
	PromptOK("GetClipOp() not implemented");
	return RectC(0, 0, 100, 100);
}

bool SvgDraw::IsPaintingOp(const Rect& r) const
{
	//todo:
	PromptOK(String().Cat() << "IsPaintingOp(" << r << ") not implemented");
	return false;
}

void SvgDraw::DrawRectOp(int x, int y, int cx, int cy, Color color)
{
	StringBuffer style;
	style << "stroke:none;";
	style << "fill:rgb(" << color.GetR() << ',' << color.GetG() << ',' << color.GetB() << ");";
	
	XmlTag rect("rect");
	rect("x", x)("y", y)("width", cx)("height", cy)("style", style);
	AddTag(rect);
}

void SvgDraw::DrawImageOp(int x, int y, int cx, int cy, const Image& img, const Rect& src, Color color)
{
	//TODO: src, color
	PNGEncoder pngEncoder;
	String pngString = pngEncoder.SaveString(img);
	String dataUri = "data:image/png;base64," + Base64Encode(pngString);
	
	XmlTag image("image");
	image("x", x)("y", y)("width", cx)("height", cy)("xlink:href", dataUri);
	AddTag(image);
}

void SvgDraw::DrawLineOp(int x1, int y1, int x2, int y2, int width, Color color)
{
	//TODO: Why is the standard width = 0  ?!?
	StringBuffer style;
	style << "stroke:rgb(" << color.GetR() << ',' << color.GetG() << ',' << color.GetB() << ");";
	style << "stroke-width:" << width << ';';
	style << "stroke-linecap:round;";
	
	XmlTag line("line");
	line("x1", x1)("y1", y1)("x2", x2)("y2", y2)("style", style);
	AddTag(line);
}

void SvgDraw::DrawPolyPolylineOp(const Point *vertices, int vertex_count,
                                const int *counts, int count_count,
                                int width, Color color, Color doxor)
{
	//todo: doxor
	StringBuffer style;
	style << "stroke:rgb(" << color.GetR() << ',' << color.GetG() << ',' << color.GetB() << ");";
	style << "stroke-width:" << width << ';';
	style << "stroke-linecap:round;stroke-linejoin:round;";
	style << "fill:none;";
	
	for (int i = 0; i < count_count; i++)
	{
		XmlTag line("polyline");
		StringBuffer points;
		
		for (int j = 0; j < counts[i]; j++, vertices++)
			points << vertices->x << ' ' << vertices->y << ' ';
		
		line("points", points)("style", style);
		AddTag(line);
	}
}

void SvgDraw::DrawPolyPolyPolygonOp(const Point *vertices, int vertex_count,
                                   const int *subpolygon_counts, int scc,
                                   const int *disjunct_polygon_counts, int dpcc,
                                   Color color, int width, Color outline,
                                   uint64 pattern, Color doxor)
{
	//todo: disjunct polygons, dpcc, pattern, doxor
	
	bool fill = !IsNull(color);
	bool stroke = !IsNull(outline) && !IsNull(width);
	if (!fill && !stroke) return;
	
	StringBuffer style;
	if (fill)
		style << "fill:rgb(" << color.GetR() << ',' << color.GetG() << ',' << color.GetB() << ");";
	if (stroke)
		style << "stroke:rgb(" << outline.GetR() << ',' << outline.GetG() << ',' << outline.GetB() << ");";
	if (stroke)
		style << "stroke-width:" << width << ';';
	
	for (int i = 0; i < scc; i++)
	{
		XmlTag polygon("polygon");
		StringBuffer points;
		
		for (int j = 0; j < subpolygon_counts[i]; j++, vertices++)
			points << vertices->x << ' ' << vertices->y << ' ';
		
		polygon("points", points)("style", style);
	}
}

void SvgDraw::DrawEllipseOp(const Rect& r, Color color, int pen, Color pencolor)
{
	//TODO: why is the pen default Null and not 0 or 1?
	if (pen == (int)Null)
		pen = 0;
	
	StringBuffer style;
	style << "stroke:rgb(" << pencolor.GetR() << ',' << pencolor.GetG() << ',' << pencolor.GetB() << ");";
	style << "stroke-width:" << pen << ';';
	style << "fill:rgb(" << color.GetR() << ',' << color.GetG() << ',' << color.GetB() << ");";
	
	XmlTag ellipse("ellipse");
	ellipse("cx", r.left + r.GetWidth() / 2)
		   ("cy", r.top + r.GetHeight() / 2)
		   ("rx", r.GetWidth() / 2)
		   ("ry", r.GetHeight() / 2)
		   ("style", style);
	AddTag(ellipse);
}

void SvgDraw::DrawArcOp(const Rect& rc, Point start, Point end, int width, Color color)
{
	PromptOK("DrawArcOp not implemented");
}

void SvgDraw::DrawTextOp(int x, int y, int angle, const wchar *text, Font font,
	                    Color ink, int n, const int *dx)
{
	//TODO: angle
	StringBuffer style;
	style << "stroke:none;";
	style << "fill:rgb(" << ink.GetR() << ',' << ink.GetG() << ',' << ink.GetB() << ");";
	style << "font-size:" << font.GetHeight() << "px;";
	style << "font-family:" << Font::GetFaceName(font.GetFace()) << ";";
	if (font.IsBold())
		style << "font-weight:bold;";
	if (font.IsItalic())
		style << "font-style:italic;";
	if (font.IsUnderline() && font.IsStrikeout())
		style << "text-decoration:underline line-through;";
	else if (font.IsUnderline())
		style << "text-decoration:underline;";
	else if (font.IsStrikeout())
		style << "text-decoration:line-through;";
	
	String xAttr;
	xAttr << x;
	if (dx != NULL)
	{
		int tempX = x;
		for (int i = 0; i < n; i++)
		{
			tempX += dx[i];
			xAttr << " " << tempX;
		}
	}
	
	XmlTag t("text");
	t("x", xAttr)("y", y + font.GetHeight())("style", style);
	AddTag(t, WString(text).ToString());
}

String SvgDraw::GetNextId()
{
	return String().Cat() << "id" << _nextId++;
}

END_UPP_NAMESPACE
