content: /*
   * Copyright 2015-2016 Alessio Vinerbi. All rights reserved.
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library 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
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   * MA 02110-1301  USA
   */
  /*
   * To change this license header, choose License Headers in Project Properties.
   * To change this template file, choose Tools | Templates
   * and open the template in the editor.
   */
  package com.anchorage.docks.containers;
  
  import static com.anchorage.docks.containers.common.AnchorageSettings.FLOATING_NODE_DROPSHADOW_RADIUS;
  import static com.anchorage.docks.containers.common.AnchorageSettings.FLOATING_NODE_MINIMUM_HEIGHT;
  import static com.anchorage.docks.containers.common.AnchorageSettings.FLOATING_NODE_MINIMUM_WIDTH;
  import com.anchorage.docks.node.DockNode;
  import com.anchorage.docks.stations.DockStation;
  import javafx.event.EventHandler;
  import javafx.geometry.Insets;
  import javafx.geometry.Point2D;
  import javafx.scene.Cursor;
  import javafx.scene.Scene;
  import javafx.scene.SnapshotParameters;
  import javafx.scene.effect.BlurType;
  import javafx.scene.effect.DropShadow;
  import javafx.scene.image.ImageView;
  import javafx.scene.image.WritableImage;
  import javafx.scene.input.MouseEvent;
  import javafx.scene.layout.StackPane;
  import javafx.scene.paint.Color;
  import javafx.stage.Stage;
  import javafx.stage.StageStyle;
  import javafx.stage.Window;
  
  /**
   *
   * @author avinerbi
   */
  public class StageFloatable extends Stage {
  
      private Scene scene;
      private DockNode node;
      private StackPane transparentRootPanel;
      private StackPane stackPanelContainer;
      private Window owner;
  
      private Point2D mousePositionStart;
      private EventHandler<MouseEvent> eventsHandler;
  
      private double startX;
      private double startWidth;
  
      private double startY;
      private double startHeight;
  
      private ImageView imageView;
      private WritableImage ghostImage;
  
      private StageFloatable() {
  
      }
  
      public StageFloatable(DockNode node, Window owner, double startX, double startY) {
          this.node = node;
          this.owner = owner;
  
          buildUI(startX, startY);
  
          setAlwaysOnTop(true);
      }
  
      private void setupMouseEvents() {
          eventsHandler = event -> {
  
              if (event.getEventType() == MouseEvent.MOUSE_PRESSED) {
                  mousePositionStart = new Point2D(event.getScreenX(), event.getScreenY());
                  startWidth = getWidth();
                  startX = getX();
  
                  startHeight = getHeight();
                  startY = getY();
              }
  
              if (event.getEventType() == MouseEvent.MOUSE_MOVED) {
  
                  boolean sizeRight = valueInRange(event.getX(), stackPanelContainer.getWidth() - stackPanelContainer.getPadding().getLeft(), stackPanelContainer.getWidth());
                  boolean sizeLeft = valueInRange(event.getX(), 0, stackPanelContainer.getPadding().getRight());
                  boolean sizeTop = valueInRange(event.getY(), 0, stackPanelContainer.getPadding().getTop());
                  boolean sizeBottom = valueInRange(event.getY(), stackPanelContainer.getHeight() - stackPanelContainer.getPadding().getBottom(), stackPanelContainer.getHeight());
  
                  Cursor cursor = changeCursor(sizeLeft, sizeRight, sizeTop, sizeBottom);
  
                  getScene().setCursor(cursor);
              }
  
              if (event.getEventType() == MouseEvent.MOUSE_DRAGGED && (getScene().getCursor() != null && getScene().getCursor() != Cursor.DEFAULT)) {
  
                  if (getScene().getCursor() == Cursor.E_RESIZE || getScene().getCursor() == Cursor.SE_RESIZE || getScene().getCursor() == Cursor.NE_RESIZE) {
                      if (event.getScreenX() - getX() + FLOATING_NODE_DROPSHADOW_RADIUS > FLOATING_NODE_MINIMUM_WIDTH) {
                          setWidth(event.getScreenX() - getX() + FLOATING_NODE_DROPSHADOW_RADIUS);
                      }
                  }
                  else if (getScene().getCursor() == Cursor.S_RESIZE || getScene().getCursor() == Cursor.SE_RESIZE || getScene().getCursor() == Cursor.SW_RESIZE) {
                      if (event.getScreenY() - getY() + FLOATING_NODE_DROPSHADOW_RADIUS > FLOATING_NODE_MINIMUM_HEIGHT) {
                          setHeight(event.getScreenY() - getY() + FLOATING_NODE_DROPSHADOW_RADIUS);
                      }
                  }
                  else if (getScene().getCursor() == Cursor.W_RESIZE || getScene().getCursor() == Cursor.NW_RESIZE || getScene().getCursor() == Cursor.SW_RESIZE) {
  
                      double newX = event.getScreenX() - FLOATING_NODE_DROPSHADOW_RADIUS;
                      double newWidth = startX - newX + startWidth;
  
                      if (newWidth > FLOATING_NODE_MINIMUM_WIDTH) {
                          setX(newX);
                          setWidth(newWidth);
                      }
  
                  }
                  else if (getScene().getCursor() == Cursor.N_RESIZE || getScene().getCursor() == Cursor.NW_RESIZE || getScene().getCursor() == Cursor.NE_RESIZE) {
  
                      double newY = event.getScreenY() - FLOATING_NODE_DROPSHADOW_RADIUS;
                      double newHeight = startY - newY + startHeight;
  
                      if (newHeight > FLOATING_NODE_MINIMUM_HEIGHT) {
                          setY(newY);
                          setHeight(newHeight);
                      }
  
                  }
  
              }
              /*
              else if (event.getEventType() == MouseEvent.MOUSE_RELEASED) {
                  // TODO: handle this event?
              }
              */
          };
  
          stackPanelContainer.addEventFilter(MouseEvent.MOUSE_PRESSED, eventsHandler);
          stackPanelContainer.addEventFilter(MouseEvent.MOUSE_MOVED, eventsHandler);
          stackPanelContainer.addEventFilter(MouseEvent.MOUSE_DRAGGED, eventsHandler);
          stackPanelContainer.addEventFilter(MouseEvent.MOUSE_RELEASED, eventsHandler);
      }
  
      public boolean inResizing() {
          return (getScene().getCursor() != null && getScene().getCursor() != Cursor.DEFAULT);
      }
  
      private Cursor changeCursor(boolean sizeLeft, boolean sizeRight, boolean sizeTop, boolean sizeBottom) {
          Cursor cursor = Cursor.DEFAULT;
          if (sizeLeft) {
              if (sizeTop) {
                  cursor = Cursor.NW_RESIZE;
              }
              else if (sizeBottom) {
                  cursor = Cursor.SW_RESIZE;
              }
              else {
                  cursor = Cursor.W_RESIZE;
              }
          }
          else if (sizeRight) {
              if (sizeTop) {
                  cursor = Cursor.NE_RESIZE;
              }
              else if (sizeBottom) {
                  cursor = Cursor.SE_RESIZE;
              }
              else {
                  cursor = Cursor.E_RESIZE;
              }
          }
          else if (sizeTop) {
              cursor = Cursor.N_RESIZE;
          }
          else if (sizeBottom) {
              cursor = Cursor.S_RESIZE;
          }
  
          return cursor;
      }
  
      private boolean valueInRange(double value, double min, double max) {
          return (value >= min && value <= max);
      }
  
      private void removeMouseEvents() {
          removeEventFilter(MouseEvent.MOUSE_PRESSED, eventsHandler);
          removeEventFilter(MouseEvent.MOUSE_MOVED, eventsHandler);
          removeEventFilter(MouseEvent.MOUSE_DRAGGED, eventsHandler);
          removeEventFilter(MouseEvent.MOUSE_RELEASED, eventsHandler);
      }
  
      private void buildUI(double startX, double startY) {
  
          initOwner(owner);
  
          setX(startX - FLOATING_NODE_DROPSHADOW_RADIUS);
          setY(startY - FLOATING_NODE_DROPSHADOW_RADIUS);
  
          createContainerPanel();
  
          initStyle(StageStyle.TRANSPARENT);
  
          scene = new Scene(transparentRootPanel, node.getWidth() + FLOATING_NODE_DROPSHADOW_RADIUS * 2,
                            node.getHeight() + FLOATING_NODE_DROPSHADOW_RADIUS * 2,
                            Color.TRANSPARENT);
  
          setOnShown(e -> {
  
              setWidth(getWidth() + stackPanelContainer.getPadding().getLeft() + stackPanelContainer.getPadding().getRight());
              setHeight(getHeight() + stackPanelContainer.getPadding().getTop() + stackPanelContainer.getPadding().getBottom());
  
          });
  
          setScene(scene);
  
      }
  
      private void createContainerPanel() {
  
          ghostImage = node.snapshot(new SnapshotParameters(), null);
  
          imageView = new ImageView(ghostImage);
  
          stackPanelContainer = new StackPane(imageView);
  
          transparentRootPanel = new StackPane(stackPanelContainer);
  
          transparentRootPanel.setPadding(new Insets(FLOATING_NODE_DROPSHADOW_RADIUS));
  
          transparentRootPanel.setStyle("-fx-background-color:rgba(0,0,0,0);");
  
          stackPanelContainer.getStyleClass().add("docknode-floating-stack-container-panel");
  
          stackPanelContainer.setEffect(new DropShadow(BlurType.GAUSSIAN, new Color(0, 0, 0, 0.6), FLOATING_NODE_DROPSHADOW_RADIUS, 0.2, 0, 0));
  
          stackPanelContainer.relocate(FLOATING_NODE_DROPSHADOW_RADIUS, FLOATING_NODE_DROPSHADOW_RADIUS);
      }
  
      public void move(double x, double y) {
          setX(x);
          setY(y);
      }
  
      public void makeNodeActiveOnFloatableStage() {
  
          DockStation station = node.stationProperty().get();  // save the station
  
          node.undock();
  
          node.stationProperty().set(station); // resume station
  
          stackPanelContainer.getChildren().remove(imageView);
  
          stackPanelContainer.getChildren().add(node);
  
          if (node.resizableProperty().get()) {
              setupMouseEvents();
          }
          
           
      }
  
      public void closeStage() {
  
          transparentRootPanel.getChildren().removeAll();
          setScene(null);
          hide();
      }
  
      public void makeFloatable() {
  
      }
  
      public Insets getPaddingOffset() {
          return stackPanelContainer.getPadding();
      }
  
  }
  