package schema;

import model.*;

@:expose
class MCDToSVG {
	/**
	 * génère un diagramme au format SVG à partir d'un MCD
	 * @param diagram le MCD
	 * @return le diagramme SVG (sous la forme d'un objet XML)
	 */
	public static function generateSVG(diagram : Diagram) : Xml {
		var doc : Xml = Xml.createDocument();
		var svg : Xml = Svg.initSvg(doc);

		for (set in diagram) {
			if (Std.isOfType(set, Association)) {
				set.svg = generateAssociationSVG(cast(set, Association));
			} else {
				set.svg = generateSetSVG(set);
			}
			svg.addChild(set.svg);
		}

		Svg.placeObjects(diagram, svg, diagram.height, diagram.width);
		drawLines(diagram, svg);
		return doc;
	}

	static function generateAssociationSVG(set : Association) : Xml {
		var fieldsCount : Int = 0;
		var chrWidth : Int = set.name.length;
		for (field in set.fields) {
			fieldsCount++;
			if (field.name.length > chrWidth) {
				chrWidth = field.name.length;
			}
		}
		var g : Xml = Xml.createElement("g");
		if (set.isAgregation) {
			var rect : Xml = Xml.createElement("rect");
			g.addChild(rect);
			rect.set("height", Std.string((fieldsCount == 0 ? 2 : (fieldsCount + 1)) * Svg.LETTER_HEIGHT + Svg.PADDING * 2));
			rect.set("width", Std.string(chrWidth * Svg.LETTER_WIDTH + Svg.PADDING * 2 + Svg.RXY / 2));
			rect.set("style", Svg.FILL_WHITE_STYLE);
		}
		var rect : Xml = Xml.createElement("rect");
		g.addChild(rect);
		rect.set("height", Std.string((fieldsCount == 0 ? 2 : (fieldsCount + 1)) * Svg.LETTER_HEIGHT + Svg.PADDING * 2));
		rect.set("width", Std.string(chrWidth * Svg.LETTER_WIDTH + Svg.PADDING * 2 + Svg.RXY / 2));
		rect.set("rx", Std.string(Svg.RXY));
		rect.set("ry", Std.string(Svg.RXY));
		rect.set("style", Svg.FILL_GREY_STYLE);

		var line : Xml = Xml.createElement("line");
		g.addChild(line);
		line.set("x1", "0");
		line.set("x2", Std.string(chrWidth * Svg.LETTER_WIDTH + Svg.PADDING * 2 + Svg.RXY / 2));
		line.set("y1", Std.string(Svg.LETTER_HEIGHT + Svg.PADDING));
		line.set("y2", Std.string(Svg.LETTER_HEIGHT + Svg.PADDING));

		var title : Xml = Xml.createElement("text");
		g.addChild(title);
		title.addChild(Xml.createPCData(set.name));
		title.set("x", Std.string(Svg.PADDING + Svg.RXY / 4 + (chrWidth - set.name.length) * Svg.LETTER_WIDTH / 2));
		title.set("y", Std.string(Svg.LETTER_HEIGHT + 1));

		var text : Xml = Xml.createElement("text");
		g.addChild(text);
		for (i in 0...set.fields.length) {
			var tspan : Xml = Xml.createElement("tspan");
			text.addChild(tspan);
			tspan.set("x", Std.string(Svg.PADDING + Svg.RXY / 4));
			tspan.set("y", Std.string(Svg.LETTER_HEIGHT * (i + 2) + Svg.PADDING));
			tspan.addChild(Xml.createPCData(set.fields[i].name));
		}
		return g;
	}

	static function generateSetSVG(set : Set) : Xml {
		var fieldsCount : Int = 0;
		var chrWidth : Int = set.name.length;
		for (field in set.fields) {
			if (!field.isStatic) {
				fieldsCount++;
				if (field.name.length > chrWidth) {
					chrWidth = field.name.length;
				}
			}
		}
		var g : Xml = Xml.createElement("g");
		var rect : Xml = Xml.createElement("rect");
		g.addChild(rect);
		rect.set("height", Std.string((fieldsCount == 0 ? 2 : (fieldsCount + 1)) * Svg.LETTER_HEIGHT + Svg.PADDING * 2));
		rect.set("width", Std.string(Std.int(chrWidth * Svg.LETTER_WIDTH + Svg.PADDING * 2)));
		rect.set("style", Svg.FILL_WHITE_STYLE);

		var titleBG : Xml = Xml.createElement("rect");
		g.addChild(titleBG);
		titleBG.set("height", Std.string(Svg.LETTER_HEIGHT + Svg.PADDING));
		titleBG.set("width", Std.string(chrWidth * Svg.LETTER_WIDTH + Svg.PADDING * 2));
		titleBG.set("style", Svg.FILL_GREY_STYLE);

		var title : Xml = Xml.createElement("text");
		g.addChild(title);
		title.addChild(Xml.createPCData(set.name));
		title.set("x", Std.string(Svg.PADDING + (chrWidth - set.name.length) * Svg.LETTER_WIDTH / 2));
		title.set("y", Std.string(Svg.LETTER_HEIGHT));

		var text : Xml = Xml.createElement("text");
		g.addChild(text);
		for (i in 0...set.fields.length) {
			if (!set.fields[i].isStatic) {
				var tspan : Xml = Xml.createElement("tspan");
				text.addChild(tspan);
				tspan.set("x", Std.string(Svg.PADDING / 2));
				tspan.set("y", Std.string(Svg.LETTER_HEIGHT * (i + 2) + Svg.PADDING));
				if (set.id.indexOf(set.fields[i].name) != -1) {
					tspan.set("style", Svg.UNDERLINE_STYLE);
				}
				tspan.addChild(Xml.createPCData(set.fields[i].name));
			}
		}

		if (set.subsets.length != 0) {
			g.addChild(generateInheritanceSVG(set.subsetType, Std.parseInt(g.firstChild().get("width")), Std.parseInt(g.firstChild().get("height"))));
		}
		return g;
	}

	static function generateInheritanceSVG(type : String, setWidth : Int, setHeight : Int) : Xml {
		//~ var chrWidth = 2; //<path d="M125,85 a1,1 0 1,0 -215,0"
		var w : Int = Std.int(2 * Svg.LETTER_WIDTH + Svg.PADDING * 2 + Svg.RXY / 2);
		var h : Int = Std.int(w / 2);
		var g : Xml = Xml.createElement("g");

		var line : Xml = Xml.createElement("line");
		g.addChild(line);
		line.set("x1", Std.string(Std.int(w / 2)));
		line.set("x2", Std.string(Std.int(w / 2)));
		line.set("y1", "0");
		line.set("y2", Std.string(Svg.PADDING)); //Svg.MARGIN for a box

		var path : Xml = Xml.createElement("path");
		g.addChild(path);
		path.set("d", 'M $w ${h + Svg.PADDING} a1,1 0 1,0 -$w 0 l $w 0'); //Svg.MARGIN for a box
		path.set("style", Svg.FILL_GREY_STYLE);

		var title : Xml = Xml.createElement("text");
		g.addChild(title);
		title.addChild(Xml.createPCData(type));
		title.set("x", Std.string(Svg.PADDING + Svg.RXY / 4 + (2 - type.length) * Svg.LETTER_WIDTH / 2));
		title.set("y", Std.string(Svg.LETTER_HEIGHT + 1 + Svg.PADDING)); //Svg.MARGIN for a box

		g.set("transform", "translate(" + Std.string(Std.int((setWidth - w) / 2)) + "," + Std.string(setHeight) + ")");
		return g;
	}

	static function drawLines(diagram : Diagram, svg : Xml) {
		for (assoc in diagram.associations) {
			var i : Int = 0;
			if (assoc.links.length == 2 && assoc.links[0].set.name == assoc.links[1].set.name) { //reflexive
				svg.addChild(generateLineCardsSVG(assoc, assoc.links[0].set, false, assoc.links[0].hasArrow,  assoc.links[0].getMCDCards(), 1, false));
				svg.addChild(generateLineCardsSVG(assoc, assoc.links[0].set, false, assoc.links[1].hasArrow, assoc.links[1].getMCDCards(), -1, false));
				i = 2;
			}
			for (j in i...assoc.links.length) {
				svg.addChild(generateLineCardsSVG(assoc, assoc.links[j].set, assoc.links[j].isWeak, assoc.links[j].hasArrow,
					assoc.links[j].getMCDCards(), false));
			}
		}
		for (p in diagram.sets) {
			if (p.subsets.length != 0) {
				for (c in p.subsets) {
					svg.addChild(generateLineCardsSVG(p, c, false, false, "", true));
				}
			}
		}
	}

	@SuppressWarnings("checkstyle:ParameterNumber")
	static function generateLineCardsSVG(set1 : Set, set2 : Set, isWeak : Bool, hasArrow : Bool, cards : String,
									bend : Int = 0, inheritance : Bool) : Xml {
		if (isWeak) {
			cards = "(" + cards + ")";
		}
		var line = Svg.getLine(set1, set2, "", cards, bend);
		if (inheritance) {
			var x1 : Int = Std.parseInt(Svg.coordRE.replace(set1.svg.get("transform"), "$1").split(",")[0]);
			var y1 : Int = Std.parseInt(Svg.coordRE.replace(set1.svg.get("transform"), "$1").split(",")[1]);
			var w1 : Int = Std.parseInt(set1.svg.firstChild().get("width"));
			var h1 : Int = Std.parseInt(set1.svg.firstChild().get("height"));
			line.x1 = x1 + Std.int(w1 / 2);
			line.y1 = y1 + h1 + Std.int(Svg.LETTER_WIDTH + Svg.PADDING + Svg.RXY / 4) + Svg.PADDING;
		}
		var g : Xml = Xml.createElement("g");
		g.addChild(Svg.getSvgLine(line, bend, hasArrow ? Svg.MARKERS[0].id : null));

		var mm : Xml = Xml.createElement("text");
		mm.addChild(Xml.createPCData(cards));
		//~ if (isWeak) {
			//~ mm.set("style", Svg.UNDERLINE_STYLE);
		//~ }
		mm.set("x", Std.string(line.cx2));
		mm.set("y", Std.string(line.cy2));
		g.addChild(mm);
		return g;
	}
}
