/*
 * 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.Collection;
import java.util.Optional;
import java.util.stream.Collectors;
import javafx.geometry.Bounds;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import net.sf.latexdraw.model.api.shape.Shape;
import net.sf.latexdraw.util.Flushable;

/**
 * The base class of a JFX shape view.
 * @param <S> The type of the model.
 * @author Arnaud Blouin
 */
public abstract class ViewShape<S extends Shape> extends Group implements Flushable {
	/** The model of the view. */
	protected final S model;

	/**
	 * Creates the view.
	 * @param sh The model.
	 */
	ViewShape(final S sh) {
		super();
		model = sh;

		setUserData(model);
		setFocusTraversable(false);
	}

	public Collection<javafx.scene.shape.Shape> getActivatedShapes() {
		return getActivatedGroupNodes(this);
	}

	private static Collection<javafx.scene.shape.Shape> getActivatedGroupNodes(final Group gp) {
		// Adding all the shape children
		final Collection<javafx.scene.shape.Shape> shapes = gp.getChildren().stream().filter(node -> node instanceof javafx.scene.shape.Shape && node.isVisible() && !node.isDisable()).
			map(node -> (javafx.scene.shape.Shape) node).collect(Collectors.toList());

		// Adding all the view shape children
		shapes.addAll(gp.getChildren().stream().filter(node -> node instanceof ViewShape<?> && node.isVisible() && !node.isDisable()).
			map(vs -> ((ViewShape<?>) vs).getActivatedShapes()).flatMap(st -> st.stream()).collect(Collectors.toList()));

		// Adding the shapes contained in groups that are not view shapes
		shapes.addAll(gp.getChildren().stream().filter(node -> node instanceof Group && !(node instanceof ViewShape<?>)).
			map(node -> getActivatedGroupNodes((Group) node)).flatMap(st -> st.stream()).collect(Collectors.toList()));

		// Adding the images contained in the group
		shapes.addAll(gp.getChildren().stream().filter(node -> node instanceof ImageView && node.isVisible() && !node.isDisable()).
			map(node -> {
				final Bounds bounds = node.getBoundsInParent();
				final Rectangle rec = new Rectangle(bounds.getMinX(), bounds.getMinY(), bounds.getWidth(), bounds.getHeight());
				rec.setFill(Color.WHITE);
				rec.getTransforms().setAll(gp.getLocalToSceneTransform());
				return rec;
			}).collect(Collectors.toList()));

		return shapes;
	}

	@Override
	public void flush() {
		setUserData(null);
		getChildren().clear();
		// Should be overridden to flush the bindings.
	}

	public S getModel() {
		return model;
	}

	protected Optional<Canvas> getCanvasParent() {
		Parent parent = getParent();
		while(parent != null && !(parent instanceof Canvas)) {
			parent = parent.getParent();
		}

		if(parent != null) {
			return Optional.of((Canvas) parent);
		}
		return Optional.empty();
	}
}