/*
 * This file is part of LaTeXDraw.
 * Copyright (c) 2005-2020 Arnaud BLOUIN
 * LaTeXDraw is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later version.
 * LaTeXDraw is distributed without any warranty; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 */
package net.sf.latexdraw.view.jfx;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.shape.ClosePath;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;
import javafx.scene.shape.Shape;
import javafx.scene.shape.StrokeLineCap;
import net.sf.latexdraw.model.ShapeFactory;
import net.sf.latexdraw.parser.svg.SVGAttributes;
import net.sf.latexdraw.parser.svg.SVGDocument;
import net.sf.latexdraw.parser.svg.SVGElement;
import net.sf.latexdraw.parser.svg.SVGEllipseElement;
import net.sf.latexdraw.parser.svg.SVGGElement;
import net.sf.latexdraw.parser.svg.SVGPathElement;
import net.sf.latexdraw.parser.svg.path.SVGPathSeg;
import net.sf.latexdraw.parser.svg.path.SVGPathSegClosePath;
import net.sf.latexdraw.parser.svg.path.SVGPathSegCurvetoCubic;
import net.sf.latexdraw.parser.svg.path.SVGPathSegLineto;
import net.sf.latexdraw.parser.svg.path.SVGPathSegList;
import net.sf.latexdraw.parser.svg.path.SVGPathSegMoveto;

/**
 * Converts JFX shapes to SVG elements.
 * Work in progress.
 * @author Arnaud Blouin
 */
public final class JFXToSVG {
	public static final JFXToSVG INSTANCE = new JFXToSVG();

	private JFXToSVG() {
		super();
	}

	public List<SVGElement> shapesToElements(final List<Node> nodes, final SVGDocument doc) {
		if(nodes == null) {
			return Collections.emptyList();
		}
		return nodes.stream().filter(node -> !node.isDisable() && node.isVisible()).map(shape -> {
			if(shape instanceof Shape) {
				return shapeToElement((Shape) shape, doc);
			}
			if(shape instanceof Group) {
				return groupToSVGGElement((Group) shape, doc);
			}
			return null;
		}).filter(elt -> elt != null).collect(Collectors.toList());
	}

	public SVGGElement groupToSVGGElement(final Group group, final SVGDocument doc) {
		final SVGGElement g = new SVGGElement(doc);
		shapesToElements(group.getChildren(), doc).forEach(elt -> g.appendChild(elt));
		return g;
	}

	public SVGElement shapeToElement(final Shape shape, final SVGDocument doc) {
		if(shape instanceof Path) {
			return pathToSVGPath((Path) shape, doc);
		}
		if(shape instanceof Ellipse) {
			return ellipseToSVGEllipse((Ellipse) shape, doc);
		}
		return null;
	}

	private void copyPropertiesToSVG(final SVGElement elt, final Shape shape) {
		elt.setStrokeWidth(shape.getStrokeWidth());
		elt.setStrokeLineCap(strokeLineCapToSVGLineCap(shape.getStrokeLineCap()));

		if(shape.getStroke() instanceof Color) {
			final Color col = (Color) shape.getStroke();
			elt.setStroke(ShapeFactory.INST.createColorFX(col));
			if(col.getOpacity() < 1d) {
				elt.setAttribute(SVGAttributes.SVG_STROKE_OPACITY, String.valueOf(col.getOpacity()));
			}
		}else {
			elt.setStroke(null);
		}

		if(shape.getFill() instanceof Color) {
			final Color col = (Color) shape.getFill();
			elt.setFill(ShapeFactory.INST.createColorFX(col));
			if(col.getOpacity() < 1d) {
				elt.setAttribute(SVGAttributes.SVG_FILL_OPACITY, String.valueOf(col.getOpacity()));
			}
		}else {
			elt.setFill(null);
		}
	}

	private String strokeLineCapToSVGLineCap(final StrokeLineCap cap) {
		return switch(cap) {
			case SQUARE -> SVGAttributes.SVG_LINECAP_VALUE_SQUARE;
			case BUTT -> SVGAttributes.SVG_LINECAP_VALUE_BUTT;
			case ROUND -> SVGAttributes.SVG_LINECAP_VALUE_ROUND;
		};
	}

	public SVGEllipseElement ellipseToSVGEllipse(final Ellipse ell, final SVGDocument doc) {
		final SVGEllipseElement elt = new SVGEllipseElement(ell.getCenterX(), ell.getCenterY(), ell.getRadiusX(), ell.getRadiusY(), doc);
		copyPropertiesToSVG(elt, ell);
		return elt;
	}

	/**
	 * Converts a JFX path to an SVGPath.
	 * @param path To JFX path to convert.
	 * @param doc The SVG document.
	 * @return The created SVGPath.
	 * @throws NullPointerException If one of the given parameter is null.
	 */
	public SVGPathElement pathToSVGPath(final Path path, final SVGDocument doc) {
		final SVGPathElement svgPath = new SVGPathElement(doc);
		final SVGPathSegList list = new SVGPathSegList();
		list.addAll(path.getElements().stream().map(elt -> createSVGPathSeg(elt)).filter(elt -> elt != null).collect(Collectors.toList()));
		svgPath.setPathData(list);
		copyPropertiesToSVG(svgPath, path);
		return svgPath;
	}

	private SVGPathSeg createSVGPathSeg(final PathElement elt) {
		if(elt instanceof LineTo) {
			return createSVGPathSegLineto((LineTo) elt);
		}
		if(elt instanceof MoveTo) {
			return createSVGPathSegMoveto((MoveTo) elt);
		}
		if(elt instanceof ClosePath) {
			return new SVGPathSegClosePath();
		}
		if(elt instanceof CubicCurveTo) {
			return createSVGPathSegCurvetoCubic((CubicCurveTo) elt);
		}
		return null;
	}

	public SVGPathSegLineto createSVGPathSegLineto(final LineTo elt) {
		return new SVGPathSegLineto(elt.getX(), elt.getY(), !elt.isAbsolute());
	}

	public SVGPathSegMoveto createSVGPathSegMoveto(final MoveTo elt) {
		return new SVGPathSegMoveto(elt.getX(), elt.getY(), !elt.isAbsolute());
	}

	public SVGPathSegCurvetoCubic createSVGPathSegCurvetoCubic(final CubicCurveTo elt) {
		return new SVGPathSegCurvetoCubic(elt.getX(), elt.getY(), elt.getControlX1(), elt.getControlY1(), elt.getControlX2(),
			elt.getControlY2(), !elt.isAbsolute());
	}
}