#include "Painter.h"

#define LLOG(x)

namespace Upp {

#include "SvgInternal.h"

void SvgParser::Reset()
{
	state.Add();
	State& s = state.Top();
	s.fill_gradient = s.stroke_gradient = -1;
	s.fill = Black();
	s.stroke = Null;
	s.opacity = s.fill_opacity = s.stroke_opacity = s.stroke_width = 1;
	s.dash_offset = 0;
	s.font = Serif(24);
}

void SvgParser::ProcessValue(const String& key, const String& value_)
{
	State& s = state.Top();
	String value = TrimBoth(value_);
	value = TrimBoth(value);
	LLOG("ATTR " << key << " = " << value);
	if(value != "inherit") {
		if(key == "opacity")
			s.opacity = Nvl(StrDbl(value), 1.0);
		else
		if(key == "fill") {
			if(value.StartsWith("url(#")) {
				value = value.Mid(5);
				int q = value.Find(')');
				if(q >= 0)
					value.Trim(q);
				s.fill_gradient = gradient.Find(value);
				s.fill = Null;
			}
			else {
				s.fill_gradient = -1;
				s.fill = GetColor(value);
			}
		}
		else
		if(key == "fill-opacity")
			s.fill_opacity = Nvl(StrDbl(value), 1.0);
		else
		if(key == "fill-rule")
			sw.EvenOdd(value == "evenodd");
		else
		if(key == "stroke") {
			if(value.StartsWith("url(#")) {
				value = value.Mid(5);
				int q = value.Find(')');
				if(q >= 0)
					value.Trim(q);
				s.stroke_gradient = gradient.Find(value);
				s.stroke = Null;
			}
			else {
				s.stroke = GetColor(value);
				s.stroke_gradient = -1;
			}
		}
		else
		if(key == "stroke-opacity")
			s.stroke_opacity = Nvl(StrDbl(value), 1.0);
		else
		if(key == "stroke-width")
			s.stroke_width = Nvl(StrDbl(value), 1.0);
		else
		if(key == "stroke-linecap")
			sw.LineCap(decode(value, "round", LINECAP_ROUND, "square", LINECAP_SQUARE, LINECAP_BUTT));
		else
		if(key == "stroke-linejoin")
			sw.LineJoin(decode(value, "round", LINEJOIN_ROUND, "bevel", LINEJOIN_BEVEL, LINEJOIN_MITER));
		else
		if(key == "miter-limit")
			sw.MiterLimit(max(1.0, StrDbl(value)));
		else
		if(key == "stroke-dasharray") {
			s.dash_array = value;
			sw.Dash(s.dash_array, s.dash_offset);
		}
		else
		if(key == "stroke-dashoffset") {
			s.dash_offset = StrDbl(value);
			sw.Dash(s.dash_array, s.dash_offset);
		}
		else
		if(key == "font") {
			// TODO(?)
		}
		else
		if(key == "font-family") {
			int face = Font::SANSSERIF;
			if(findarg(value, "courier", "monospace") >= 0)
				face = Font::MONOSPACE;
			if(findarg(value, "roman;serif") >= 0)
				face = Font::SERIF;
			s.font.Face(face);
		}
		else
		if(key == "font-size")
			s.font.Height(atoi(value));
		else
		if(key == "font-style")
			s.font.Italic(findarg(value, "italic", "oblique") >= 0);
		else
		if(key == "font-weight")
			s.font.Bold(findarg(value, "bold", "bolder") || atoi(value) >= 500);
   	}
}

void SvgParser::Style(const char *style)
{
	String key, value;
	for(;;) {
		if(*style == ';' || *style == '\0') {
			ProcessValue(key, value);
			value.Clear();
			key.Clear();
			if(*style == '\0')
				break;
			else
				style++;
		}
		else
		if(*style == ':') {
			key << TrimBoth(value);
			value.Clear();
			style++;
		}
		else
			value.Cat(*style++);
	}
}

Xform2D SvgParser::Transform(const char *transform)
{
	Xform2D mx;
	try {
		CParser p(transform);
		while(!p.IsEof()) {
			String kind = ToLower(p.ReadId());
			Vector<double> r;
			p.Char('(');
			while(!p.IsEof() && !p.Char(')')) {
				r.Add(p.ReadDouble());
				p.Char(',');
			}
			if(r.GetCount() >= 1) {
				LLOG("transform " << kind << r);
				if(kind == "translate" && r.GetCount() >= 2)
					mx = Xform2D::Translation(r[0], r[1]) * mx;
				else
				if(kind == "rotate") {
					if(r.GetCount() >= 3)
						mx = Xform2D::Translation(-r[1], -r[2]) * mx;
					mx = Xform2D::Rotation(r[0] * M_2PI / 360) * mx;
					if(r.GetCount() >= 3)
						mx = Xform2D::Translation(r[1], r[2]) * mx;
				}
				else
				if(kind == "scale" && r.GetCount() >= 1)
					mx = Xform2D::Scale(r[0], r[min(r.GetCount() - 1, 1)]) * mx;
				else {
					Xform2D m;
					if(kind == "skewx")
						m.x.y = atan(r[0] * M_2PI / 360);
					else
					if(kind == "skewy")
						m.y.x = atan(r[0] * M_2PI / 360);
					else
					if(kind == "matrix" && r.GetCount() >= 6) {
						m.x.x = r[0];
						m.y.x = r[1];
						m.x.y = r[2];
						m.y.y = r[3];
						m.t.x = r[4];
						m.t.y = r[5];
					}
					mx = m * mx;
				}
			}
			p.Char(',');
		}
	}
	catch(CParser::Error) {
	}
	return mx;
}

}
