"use strict";

import XMLWriter from 'xml-writer';

// import Rectangle from './Rectangle.mjs';
import Vector2 from './Vector2.mjs';

/*
 * Simplifies the process for creating an SVG dynamically.
 * Originally written for MusicBoxConverter, but lifted, reused, and extended for FloatingIslands.
 * Ported from C# to Javascript for AirQualityWeb.
 * @license MPL-2.0
 */
class SvgWriter {
	/**
	 * @param	{String}	[widthspec="100%"]
	 * @param	{String}	[heightspec="100%"]
	 * @param	{Retangle}	[viewBox=null]
	 */
	constructor(widthspec = "100%", heightspec = "100%", viewBox = null, pretty_print = false) {
		this.unitSuffix = "";
		
		// ----------------------------------
		
		this.xml = new XMLWriter(pretty_print);
	
		this.xml.startDocument();
		this.xml.writeDocType("svg", "-//W3C//DTD SVG 1.1//EN", "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd", null);
		this.xml.writeComment("Generated by SVGWriter.js, which was written by Starbeamrainbowlabs & ported from SVGWriter.cs");
		this.xml.startElement("svg", "http://www.w3.org/2000/svg");
		this.xml.writeAttribute("version", "1.1");
		this.xml.writeAttribute("width", widthspec);
		this.xml.writeAttribute("height", heightspec);

		if (viewBox != null) {
			this.xml.writeAttribute(
				"viewBox",
				`${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`
			);
		}
	}
	
	/**
	 * Completes the SVG image currently being generated and closes the underlying file stream.
	 * @return {this}
	 */
	complete() {
		this.xml.endElement();
		this.xml.endDocument();
		return this;
	}
	
	toString() {
		return this.xml.toString();
	}
	
	/**
	 * Adds a line to the image.
	 * @param	{Vector2}	start						The start position of the line.
	 * @param	{Vector2}	end							The end position of the line.
	 * @param	{String}	[strokeStyle="darkgreen"]	The colour to draw the line.
	 * @param	{Number}	[strokeWidth=3]				The width to draw the line.
	 */
	addLine(start, end, strokeStyle = "darkgreen", strokeWidth = 3) {
		this.xml.startElement("line");
		this.xml.writeAttribute("x1", `${start.X}${UnitSuffix}`);
		this.xml.writeAttribute("y1", `${start.Y}${UnitSuffix}`);
		this.xml.writeAttribute("x2", `${end.X}${UnitSuffix}`);
		this.xml.writeAttribute("y2", `${end.Y}${UnitSuffix}`);
		this.xml.writeAttribute("stroke", strokeStyle);
		this.xml.writeAttribute("stroke-width", strokeWidth.toString());
		this.xml.endElement();
		return this;
	}
	
	/**
	 * Opens a new SVG group.
	 * @param  {string} [classes=null]   The class(es) to apply to the new group.
	 * @param  {string} [transform=null] The transform(s) to apply to the new group.
	 * @return {this}
	 */
	startGroup(classes = null, transform = null) {
		this.xml.startElement("g");
		if(classes != null)
			this.xml.writeAttribute("class", classes);
		if (transform != null)
			this.xml.writeAttribute("transform", transform);
		return this;
	}
	/**
	 * Ends the most recently created unclosed group.
	 * @return {this}
	 */
	endGroup() {
		this.xml.endElement();
		return this;
	}
	/// <summary>
	/// 
	/// </summary>
	/// <param name="scale"></param>
	/**
	 * Starts a scale transform.
	 * @param  {number} scale	The scale to enlarge (or shrink!) the next items by.
	 * @return {this}	
	 */
	startScaleTransform(scale)
	{
		this.xml.startElement("g");
		this.xml.writeAttribute("transform", `scale(${scale})`);
		return this;
	}
	
	/**
	 * Ends the most recently created scale transform.
	 * @return {this}
	 */
	endTransform() {
		this.xml.endElement();
		return this;
	}
	
	/**
	 * Adds a hollow rectangle to the image.
	 * @param	{Vector2}	position	The position of the rectangle.
	 * @param	{Vector2}	size		The size of the rectangle.
	 * @param	{string}	strokeStyle	The colour to use when drawing.
	 * @param	{float}		strokeWidth	The line width to use when drawing.
	 * @return	{this}
	 */
	addRectangle(position, size, strokeStyle = "red", strokeWidth = 3) {
		this.xml.startElement("rect");
		this.xml.writeAttribute("x", `${position.x}${this.unitSuffix}`);
		this.xml.writeAttribute("y", `${position.y}${this.unitSuffix}`);
		this.xml.writeAttribute("width", `${size.X}${this.unitSuffix}`);
		this.xml.writeAttribute("height", `${size.Y}${this.unitSuffix}`);
		this.xml.writeAttribute("fill", "none");
		this.xml.writeAttribute("stroke", strokeStyle);
		this.xml.writeAttribute("stroke-width", strokeWidth.toString());
		this.xml.endElement();
		return this;
	}
	/**
	 * Adds a circle to the image.
	 * @param	{Vector2}	centre				The position of the centre of the circle.
	 * @param	{Number}	radius				The radius of the circle.
	 * @param	{String}	[fillStyle="blue"]	The colour to fill the circle with.
	 * @return	{this}
	 */
	addCircle(centre, radius, fillStyle = "blue") {
		this.xml.startElement("circle");
		this.xml.writeAttribute("cx", `${centre.x}${this.unitSuffix}`);
		this.xml.writeAttribute("cy", `${centre.y}${this.unitSuffix}`);
		this.xml.writeAttribute("r", `${radius}${this.unitSuffix}`);
		this.xml.writeAttribute("fill", fillStyle);
		this.xml.endElement();
		return this;
	}
	
	/**
	 * Adds a solid n-sided polygon to the image.
	 * @param	{string}	fillStyle	The colour to fill the polygon with.
	 * @param	{Vector2[]}	points		The co-ordinates that make up the polygon.
	 * @return	{this}
	 */
	addPolygon(fillStyle, points) {
		this.xml.startElement("polygon");
		this.xml.writeAttribute("fill", fillStyle);
		this.xml.writeAttribute(
			"points",
			points.map((point) => `${point.x},${point.y}`).join(" ")
		);
		this.xml.endElement();
		return this;
	}
	
	/**
	 * Adds an isosceles / equilateral triangle to the image.
	 * This is a shorthand method that calls AddPolygon() under-the-hood.
	 * @param	{Vector2}	position	The position to draw the triangle at.
	 * @param	{Number}	baseWidth	The width of the triangle's base.
	 * @param	{Number}	height		The height of the triangle.
	 * @param	{Boolean}	upsideDown	If set to true, then the triangle will point downwards instead of upwards.
	 * @param	{string}	fillStyle	The colour to fill the triangle with.
	 * @return	{this}
	 */
	addTriangleRegular(position, baseWidth, height, upsideDown, fillStyle)
	{
		this.addPolygon(
			fillStyle,
			position.subtract(new Vector2(baseWidth / 2, 0)),
			position.add(new Vector2(baseWidth / 2, 0)),
			position.subtract(new Vector2(0, upsideDown ? -height : height))
		);
		return this;
	}
}

SvgWriter.string2element = (svg_string) => {
	let temp = document.createElement("div");
	temp.innerHTML = svg_string;
	return temp.querySelector("svg");
}

export default SvgWriter;