#include "DocSVG.h"
#include "Util.h"


const char SVGOperation::PathOperationChar[] =
{
	'M', 'L', 'H', 'V', 'C', 'S', 'Q', 'T', 'A', 'Z',
	'm', 'l', 'h', 'v', 'c', 's', 'q', 't', 'a', 'z',   ' ', ' ', ' '
};


const int SVGOperation::PathOperationArgs[] =
{
	2,   2,   1,   1,   6,   4,   4,   2,   7,   0,
	2,   2,   1,   1,   6,   4,   4,   2,   7,   0,     0,  0,  0
};


DocSVG::DocSVG(double a_scale) : m_scale(a_scale)
{
  currentGradient = nullptr;
}


DocSVG::~DocSVG()
{
}


/// Visit a document.
bool DocSVG::VisitEnter( const TiXmlDocument& /* doc */ )
{
	return true;
}


/// Visit a document.
bool DocSVG::VisitExit( const TiXmlDocument& /* doc */ )
{
	return true;
}


/// Visit a declaration
bool DocSVG::Visit( const TiXmlDeclaration& /* declaration */ )
{
	return true;
}


/// Visit a stylesheet reference
bool DocSVG::Visit( const TiXmlStylesheetReference& /* stylesheet */ )
{
	return true;
}


/// Visit a comment node
bool DocSVG::Visit( const TiXmlComment& /* comment */ )
{
	return true;
}


/// Visit an unknown node
bool DocSVG::Visit( const TiXmlUnknown& /* unknown */ )
{
	return true;
}


// static
DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str)
{
	SVGStyle style = { 0.0, 0.0, 0, "", 0.0, 0, 0.0, 0, 0 };
	std::vector<std::string> list = split(a_str, ';');
	for (size_t i = 0; i < list.size(); ++i)
	{
		std::vector<std::string> kvp = split(list[i], ':');
		std::string s = kvp[0];
		if (s == "opacity")              style.opacity        = str2float(kvp[1]);
		else if (s == "fill-opacity")    style.fillOpacity    = str2float(kvp[1]);
    else if (s == "fill")            style.fillString     = kvp[1], style.fillColor = parseColor(kvp[1].c_str());
		else if (s == "stroke-opacity")  style.strokeOpacity  = str2float(kvp[1]);
		else if (s == "stroke")          style.strokeColor    = parseColor(kvp[1].c_str());
		else if (s == "stroke-width")    style.strokeWidth    = str2float(kvp[1]);
		else if (s == "stroke-linecap")  style.strokeLineCap  = parseLineCap(kvp[1]);
		else if (s == "stroke-linejoin") style.strokeLineJoin = parseLineJoin(kvp[1]);
	}
	return style;
}


// static
uint32_t DocSVG::parseLineCap(const std::string& s)
{
	if (s == "butt")         return 0;
	else if (s == "round")   return 1;
	else if (s == "square")  return 2;
  printf("Unknown line-cap: %s\n", s.c_str());
  return -1;
}


// static
uint32_t DocSVG::parseLineJoin(const std::string& s)
{
	if (s == "round")       return 0;
	else if (s == "bevel")  return 1;
	else if (s == "miter")  return 2;
  printf("Unknown line-join: %s\n", s.c_str());
  return -1;
}


// static
uint32_t DocSVG::parseColor(const char* a_str)
{
	std::string s = a_str;
	if (s == "red")          return 0xffff0000;
	else if (s == "blue")    return 0xff0000ff;
	else if (s == "green")   return 0xff00ff00;
	else if (s == "yellow")  return 0xffffff00;
	else if (s == "purple")  return 0xffe00fe0;
	else if (s == "cyan")    return 0xff00ffff;
	else if (s == "magenta") return 0xffff00ff;
	else if (s == "black")   return 0xff000000;
	else if (s == "white")   return 0xffffffff;
	else if (s == "none")    return 0;
	return str2col(s);
}


/// Visit an element.
bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute )
{

// Inkscape:
// 	style="fill:#729fcf;stroke:none"
// Spec:
//	fill="yellow" stroke="blue" stroke-width="5"

	SVGPath path;

	if (strcmp(element.ValueTStr().c_str(), "stop") == 0) {
    if (currentGradient != nullptr)
    {
      SVGColorStop stop;
      const TiXmlAttribute* attrib = firstAttribute;
      while ( attrib ) {
        if (strcmp(attrib->Name(), "offset") == 0) {
          stop.offset = str2float(attrib->Value());
        } else if (strcmp(attrib->Name(), "style") == 0) {
          std::vector<std::string> list = split(attrib->Value(), ';');
          for (size_t i = 0; i < list.size(); ++i)
          {
            std::vector<std::string> kvp = split(list[i], ':');
            std::string s = kvp[0];
            if (s == "stop-color")           stop.color   = parseColor(kvp[1].c_str());
            else if (s == "stop-opacity")    stop.opacity = str2float(kvp[1]);
          }
        }
        attrib = attrib->Next();
      }
      currentGradient->stops.push_back(stop);
    }
  }

	if (strcmp(element.ValueTStr().c_str(), "linearGradient") == 0) {
    SVGGradient gradient;
		const TiXmlAttribute* attrib = firstAttribute;
		while ( attrib ) {
			if (strcmp(attrib->Name(), "id") == 0) {
				gradient.name = attrib->Value();
      } else if (strcmp(attrib->Name(), "xlink:href") == 0) {
        gradient.stops = gradients[attrib->Value() + 1].stops;
      } else if (strcmp(attrib->Name(), "x1") == 0) {
        gradient.x1 = str2float(attrib->Value());
      } else if (strcmp(attrib->Name(), "y1") == 0) {
        gradient.y1 = str2float(attrib->Value());
      } else if (strcmp(attrib->Name(), "x2") == 0) {
        gradient.x2 = str2float(attrib->Value());
      } else if (strcmp(attrib->Name(), "y2") == 0) {
        gradient.y2 = str2float(attrib->Value());
      }
			attrib = attrib->Next();
    }
    gradients[gradient.name] = gradient;
    currentGradient = &gradients[gradient.name];
  }
  
	if (strcmp(element.ValueTStr().c_str(), "path") == 0) {
		const TiXmlAttribute* attrib = firstAttribute;
		while ( attrib ) {
			if (strcmp(attrib->Name(), "d") == 0) {
				path.operations = ParsePath(attrib->Value());
			} else if (strcmp(attrib->Name(), "style") == 0) {
				path.style = parseStyle(attrib->Value());
			} else if (strcmp(attrib->Name(), "fill") == 0) {
				path.style.fillColor = parseColor(attrib->Value());
			} else if (strcmp(attrib->Name(), "stroke") == 0) {
				path.style.strokeColor = parseColor(attrib->Value());
			} else if (strcmp(attrib->Name(), "stroke-width") == 0) {
				path.style.strokeWidth = str2float(attrib->Value());
			}
			attrib = attrib->Next();
		}
	}

  if (strcmp(element.ValueTStr().c_str(), "rect") == 0) {
		const TiXmlAttribute* attrib = firstAttribute;
    float x, y, w, h;
		while ( attrib ) {
			if (strcmp(attrib->Name(), "x") == 0) {
				x = str2float(attrib->Value());
      } else if (strcmp(attrib->Name(), "y") == 0) {
				y = str2float(attrib->Value());
      } else if (strcmp(attrib->Name(), "width") == 0) {
				w = str2float(attrib->Value());
      } else if (strcmp(attrib->Name(), "height") == 0) {
				h = str2float(attrib->Value());
			} else if (strcmp(attrib->Name(), "style") == 0) {
				path.style = parseStyle(attrib->Value());
			} else if (strcmp(attrib->Name(), "fill") == 0) {
				path.style.fillColor = parseColor(attrib->Value());
			} else if (strcmp(attrib->Name(), "stroke") == 0) {
				path.style.strokeColor = parseColor(attrib->Value());
			} else if (strcmp(attrib->Name(), "stroke-width") == 0) {
				path.style.strokeWidth = str2float(attrib->Value());
			}
			attrib = attrib->Next();
		}
		
    SVGOperation currentOp;
		currentOp.m_type = SVGOperation::BeginPathOperation;
		path.operations.push_back(currentOp);

    currentOp.m_type = SVGOperation::MoveToAbs;
    currentOp.m_values[0] = x;
    currentOp.m_values[1] = y;
		path.operations.push_back(currentOp);

    currentOp.m_type = SVGOperation::HorizontalLineToRel;
    currentOp.m_values[0] = w;
		path.operations.push_back(currentOp);
		
    currentOp.m_type = SVGOperation::VerticalLineToRel;
    currentOp.m_values[0] = h;
		path.operations.push_back(currentOp);

    currentOp.m_type = SVGOperation::HorizontalLineToRel;
    currentOp.m_values[0] = -w;
		path.operations.push_back(currentOp);
    
    currentOp.m_type = SVGOperation::ClosePath;
		path.operations.push_back(currentOp);

		currentOp.m_type = SVGOperation::EndPathOperation;
		path.operations.push_back(currentOp);
	}

	paths.push_back(path);

	return true;
}


/// Visit an element.
bool DocSVG::VisitExit( const TiXmlElement& /* element */ )
{
	return true;
}


/// Visit a text node
bool DocSVG::Visit( const TiXmlText& /* text */ )
{
	return true;
}

