/*
Reversound is used to get the music sheet of a piece from a music file.
Copyright (C) 2014  Gabriel AUGENDRE
Copyright (C) 2014  Gabriel DIENY
Copyright (C) 2014  Arthur GAUCHER
Copyright (C) 2014  Gabriel LEPETIT-AIMON

This program 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 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but 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.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package gui.graphs;

import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.scene.effect.DropShadow;
import javafx.scene.input.ContextMenuEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;


/**
 * Created by gaby on 11/05/14.
 * Classe permettant d'afficher un overlay au dessus du graphs principal dans une GraphicView
 */
public class OverlayGraph extends BorderPane implements ChangeListener<Number>, EventHandler<MouseEvent> {
	private AbstractGraph graph;

	private GraphicView graphicView;

	private AutoPosition autoPosition = AutoPosition.CUSTOM;

	private double xMousePos=0;
	private double yMousePos=0;
	private double xMouseOffset=0;
	private double yMouseOffset=0;

	public OverlayGraph() {
		super();
		Platform.runLater(new Runnable() {
			@Override
			public void run() {

				getStyleClass().add("overlay");
				getStylesheets().add(AmpliView.class.getResource("Overlay.css").toExternalForm());

				DropShadow dS = new DropShadow();
				dS.setRadius(10);
				dS.setOffsetY(2);
				dS.setOffsetX(2);
				setEffect(dS);
			}
		});
	}

	public OverlayGraph(AbstractGraph graph){
		this();
		setGraph(graph);
	}
	public OverlayGraph(AbstractGraph graph, GraphicView graphicView){
		this(graph);
		setGraphicView(graphicView);
	}

	public void setGraph(AbstractGraph graph){
		if(graph==null)
			return;

		if(this.graph!=null)
			this.graph.setVisibility(false);
		graph.setVisibility(getVisibility());
		this.graph = graph;

		Platform.runLater(new Runnable() {
			@Override
			public void run() {
				setCenter(graph.getMainNode());
			}
		});
	}

	static public OverlayGraph centeredOverlay(AbstractGraph graph, int height, int width){
		OverlayGraph r = new OverlayGraph(graph);
		r.setOverlayHeight(height);
		r.setOverlayWidth(width);
		r.setAutoPosition(AutoPosition.CENTERED);
		return r;
	}
	static public OverlayGraph perfectCenteredOverlay(AbstractGraph graph){
		OverlayGraph r = new OverlayGraph(graph);
		r.setAutoPosition(AutoPosition.PERFECT_CENTERED);
		return r;
	}
	static public OverlayGraph followMouseGraph(AbstractGraph graph, int height, int width){
		OverlayGraph r = new OverlayGraph(graph);
		r.setAutoPosition(AutoPosition.MOUSE);
		r.setOverlayHeight(height);
		r.setOverlayWidth(width);
		return r;
	}

	public AbstractGraph getGraph() {
		return graph;
	}

	public boolean getVisibility(){return isVisible();}

	public void setVisibility(boolean visibility){
		setVisible(visibility);
		graph.setVisibility(visibility);

	}

	public void viewVisibiliyChange(){
		graph.setVisibility(getVisibility()||graphicView.getVisibility());
	}


	/**
	 * Fait apparaitre le graph à la position indiqué
	 * Si le positionnement automatique est en mode MOUSE la position de l'overlay est forcé
	 * Si il est en mode CENTERED ou PERFECT_CENTERED, le passe automatiquement en mode CUSTOM
	 * @param x Position horizontale absolue dans la GraphicView
	 * @param y Position verticale absolue dans la GraphicView
	 */
	public void popupAt(double x, double y){
		if(autoPosition == AutoPosition.CENTERED || autoPosition==AutoPosition.PERFECT_CENTERED)
			setAutoPosition(AutoPosition.CUSTOM);

		if(autoPosition==AutoPosition.CUSTOM) {
			setTranslateX(x);
			setTranslateY(y);
		}else{
			xMousePos = x;
			yMousePos = y;
			moveToMouse();
		}
	}

	/**
	 * Modifie la position horizontale de l'overlay.
	 * Passe automatiquement le positionnement automatique en mode CUSTOM
	 * @param x Position horizontale absolue dans la GraphicView
	 */
	public void setX(double x){
		if(autoPosition!=AutoPosition.CUSTOM)
			setAutoPosition(AutoPosition.CUSTOM);
		setTranslateX(x);
	}
	/**
	 * Modifie la position verticale de l'overlay.
	 * Passe automatiquement le positionnement automatique en mode CUSTOM
	 * @param y Position verticale absolue dans la GraphicView
	 */
	public void setY(double y){
		if(autoPosition!=AutoPosition.CUSTOM)
			setAutoPosition(AutoPosition.CUSTOM);
		setTranslateX(y);
	}

	/**
	 * Modifie la hauteur de l'overlay
	 * Passe automatiquement le positionnement automatique en mose CENTERED si il était en mode PERFECT_CENTERED
	 * @param height Hauteur de l'overlay
	 */
	public void setOverlayHeight(double height){
		if(autoPosition==AutoPosition.PERFECT_CENTERED)
			setAutoPosition(AutoPosition.CENTERED);
		super.setPrefHeight(height);
	}
	/**
	 * Modifie la largeur de l'overlay
	 * Passe automatiquement le positionnement automatique en mose CENTERED si il était en mode PERFECT_CENTERED
	 * @param width Largeur de l'overlay
	 */
	public void setOverlayWidth(double width){
		if(autoPosition==AutoPosition.PERFECT_CENTERED)
			setAutoPosition(AutoPosition.CENTERED);
		super.setPrefWidth(width);
	}


	public double getxMouseOffset() {
		return xMouseOffset;
	}

	public void setxMouseOffset(double xMouseOffset) {
		this.xMouseOffset = xMouseOffset;
	}

	public double getyMouseOffset() {
		return yMouseOffset;
	}

	public void setyMouseOffset(double yMouseOffset) {
		this.yMouseOffset = yMouseOffset;
	}

	public void setGraphicView(GraphicView graphicView){
		if(graphicView==this.graphicView)
			return;

		if(this.graphicView!=null) {
			if (autoPosition == AutoPosition.CENTERED || autoPosition == AutoPosition.PERFECT_CENTERED) {
				graphicView.getScene().widthProperty().removeListener(this);
				graphicView.getScene().heightProperty().removeListener(this);
			}else if (autoPosition == AutoPosition.MOUSE) {
				graphicView.getScene().removeEventHandler(MouseEvent.MOUSE_MOVED, this);
			}
			this.graphicView.removeOverlay(this);

		}

		setOnContextMenuRequested(new EventHandler<ContextMenuEvent>() {
			@Override
			public void handle(ContextMenuEvent contextMenuEvent) {
				graphicView.popupOverlayContextMenu((int)contextMenuEvent.getSceneX(), (int)contextMenuEvent.getSceneY(), graph);
				contextMenuEvent.consume();
			}
		});

		this.graphicView = graphicView;
		applyConnection();
	}

	/**
	 * Modifie la propriété de placement automatique:
	 * 		-CUSTOM: laisse le choix au programateur de placé ou il veut l'overlay
	 * 		-CENTERED: centre la vue dans la fenetre (en tenant compte de la hauteur et largeur de l'overlay)
	 * 		-MOUSE: positionne l'overlay sous la souris (en tenant compte du décalage mouseOffset)
	 * @param autoPosition
	 */
	public void setAutoPosition(AutoPosition autoPosition){
		if(this.autoPosition == autoPosition)
			return;
		if(graphicView!=null) {
			if (this.autoPosition == AutoPosition.CENTERED || autoPosition == AutoPosition.PERFECT_CENTERED) {
				graphicView.getScene().widthProperty().removeListener(this);
				graphicView.getScene().heightProperty().removeListener(this);
			} else if (this.autoPosition == AutoPosition.MOUSE)
				graphicView.getScene().removeEventHandler(MouseEvent.MOUSE_MOVED, this);
		}
		this.autoPosition = autoPosition;
		applyConnection();
	}
	public AutoPosition getAutoPosition(){return autoPosition;}

	private void applyConnection(){
		if(graphicView==null)
			return;

		if(autoPosition == AutoPosition.CENTERED || autoPosition==AutoPosition.PERFECT_CENTERED) {
			graphicView.getScene().widthProperty().addListener(this);
			graphicView.getScene().heightProperty().addListener(this);
			centerGeometry();
		}


		if(autoPosition == AutoPosition.MOUSE) {
			graphicView.getScene().addEventHandler(MouseEvent.MOUSE_MOVED, this);
			moveToMouse();
		}
	}


	private void centerGeometry(){
		if(autoPosition==AutoPosition.PERFECT_CENTERED){
			Platform.runLater(new Runnable() {
				@Override
				public void run() {
					double h = graphicView.getParent().getHeight();
					double w = graphicView.getParent().getWidth();
					setTranslateX(w/20.0);
					setTranslateY(h/ 20.0);
					OverlayGraph.super.setPrefHeight(h*18.0/20.0);
					OverlayGraph.super.setPrefWidth(w*18.0/20.0);
				}
			});
		}else {
			Platform.runLater(new Runnable() {
				@Override
				public void run() {
					double h = graphicView.getParent().getHeight();
					double w = graphicView.getParent().getWidth();
					setTranslateX(w - getPrefWidth() / 2.0);
					setTranslateY(h - getPrefHeight() / 2.0);
				}
			});
		}
	}
	private void moveToMouse(){
		Platform.runLater(new Runnable() {
			@Override
			public void run() {
				setTranslateX(xMousePos - xMouseOffset);
				setTranslateY(yMousePos - yMouseOffset);
			}
		});

	}

	public enum AutoPosition{
		CUSTOM,
		CENTERED,
		PERFECT_CENTERED,
		MOUSE
	}

	@Override
	public void changed(ObservableValue<? extends Number> observableValue, Number number, Number number2) {
		centerGeometry();
	}

	@Override
	public void handle(MouseEvent mouseEvent) {
		xMousePos = mouseEvent.getX();
		yMousePos = mouseEvent.getY();
		moveToMouse();
	}

}