Legal Terms and Copyright Notice
/* 
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * Copyright © 2010, Oracle and/or its affiliates. All rights reserved.
 * Oracle is a registered trademark of Oracle Corporation and/or its affiliates. 
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
 * Other names may be trademarks of their respective owners.
 * 
 * This file is available and licensed under the following license:
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:

 *   *  Redistributions of source code must retain the above copyright notice, 
        trademark notice, this list of conditions, and the following disclaimer.

 *   *  Redistributions in binary form must reproduce the above copyright notice, 
        trademark notice, this list of conditions, and the following disclaimer in 
        the documentation and/or other materials provided with the distribution.

 *   *  Neither the name of Oracle nor the names of its contributors may be used 
        to endorse or promote products derived from this software without specific 
        prior written permission.
 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package smartcontrols;

import javafx.scene.Cursor;
import javafx.scene.CustomNode;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Resizable;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.stage.AppletStageExtension;
import javafx.stage.Stage;
import javafx.stage.StageStyle.TRANSPARENT;
import javafx.util.Math.max;


public class Application {
  public var foreground: Paint = Color.WHITE;
  public var background: Paint = Color.MIDNIGHTBLUE;

  public var size = 16.0;

  public var minWidth = 160.0;
  public var minHeight = 120.0;

  public var title: String;
  public var header: Node;
  public var content: Node;

  var applet = __PROFILE__ == "browser";
  def mobile = __PROFILE__ == "mobile";

  var hover: Boolean;
  def show: Boolean = bind not mobile and not stage.fullScreen;
  def resizable = bind show and not applet;
  def draggable = applet and AppletStageExtension.appletDragSupported;

  def round: Number = bind if (resizable) size else 0;
  def border: Number = bind if (show) size / 4 else 0;

  def width: Number = bind stage.scene.width - border - border on replace {
    def panel = content as Resizable;
    panel.width = width
  }
  def label = Label {
    textFill: bind foreground
    graphic: bind header
    width: bind width - size * 4
    text: bind if (header != null) null else title
    font: bind Font {
      name: "Arial Bold"
      size: if (mobile) size else size * 2
    }
  }
  def offset = bind max(size, label.height) + size / 2;
  def height: Number = bind stage.scene.height - border - border - offset on replace {
    def panel = content as Resizable;
    panel.height = height
  }
  def stage: Stage = Stage {
    title: bind title
    style: TRANSPARENT
    opacity: bind if (stage.containsFocus) 1.0 else 0.5
    extensions: if (mobile) null else AppletStageExtension {
      useDefaultClose: false
      shouldDragStart: function(event) {
        return hover and event.primaryButtonDown
      }
      onDragStarted: function() {
        applet = false
      }
      onAppletRestored: function() {
        applet = true
      }
    }
    scene: Scene {
      fill: null
      width: 640
      height: 480
      content: [
        Rectangle {
          var sX: Number;
          var sY: Number;

          var wX: Number;
          var wY: Number;
          var wW: Number;
          var wH: Number;

          var top: Boolean;
          var left: Boolean;
          var right: Boolean;
          var bottom: Boolean;

          var cursor: Cursor;
          cursor: bind cursor
          blocksMouse: true

          width: bind stage.scene.width
          height: bind stage.scene.height + size
          arcWidth: bind round * 1.5
          arcHeight: bind round * 1.5
          fill: bind background

          def update = function(event: MouseEvent): Void {
            if (not event.primaryButtonDown) {
              if (resizable) {
                top = event.y < size;
                left = event.x < size;
                right = event.x > stage.scene.width - size;
                bottom = event.y > stage.scene.height - size;

                if (top and left) {
                  cursor = Cursor.NW_RESIZE
                }
                else if (top and right) {
                  cursor = Cursor.NE_RESIZE
                }
                else if (bottom and left) {
                  cursor = Cursor.SW_RESIZE
                }
                else if (bottom and right) {
                  cursor = Cursor.SE_RESIZE
                }
                else if (top) {
                  cursor = Cursor.N_RESIZE
                }
                else if (left) {
                  cursor = Cursor.W_RESIZE
                }
                else if (right) {
                  cursor = Cursor.E_RESIZE
                }
                else if (bottom) {
                  cursor = Cursor.S_RESIZE
                } else {
                  cursor = null
                }
              } else {
                cursor = null
              }
            }
          }
          onMouseDragged: function(event) {
            if (top) {
              def dY = event.screenY - sY;
              stage.y = wY + dY;
              stage.height = max(minHeight, wH - dY);
            }
            else if (bottom) {
              stage.height = max(minHeight, wH + event.screenY - sY);
            }

            if (left) {
              def dX = event.screenX - sX;
              stage.x = wX + dX;
              stage.width = max(minWidth, wW - dX);
            }
            else if (right) {
              stage.width = max(minWidth, wW + event.screenX - sX);
            }
          }
          onMousePressed: function(event) {
            sX = event.screenX;
            sY = event.screenY;

            wX = stage.x;
            wY = stage.y;
            wW = stage.width;
            wH = stage.height;
          }
          onMouseReleased: update
          onMouseEntered: update
          onMouseExited: update
          onMouseMoved: update
        }
        Rectangle {
          cursor: Cursor.MOVE
          blocksMouse: true
          visible: bind show and (not applet or draggable)

          x: bind border
          y: bind border
          width: bind width
          height: bind offset
          arcWidth: bind round
          arcHeight: bind round
          fill: bind foreground
          opacity: 0.1

          onMouseEntered: function(event) {
            hover = true
          }
          onMouseExited: function(event) {
            hover = false
          }
          onMouseDragged: function(event) {
            stage.x += event.dragX;
            stage.y += event.dragY;
          }
        }
        Group {
          translateX: bind border + size / 4
          translateY: bind border + size / 4
          content: bind label
        }
        Group {
          cursor: Cursor.HAND
          blocksMouse: true
          visible: bind not applet
          translateX: bind stage.scene.width - border
          translateY: bind border + size * 0.75
          content: [
            Button {
              rotate: 45
              translateX: bind - size * 0.75
              onMouseClicked: function(event) {
                stage.close()
              }
            }
            Button {
              rotate: 90
              visible: not mobile
              translateX: bind - size * 2
              onMouseClicked: function(event) {
                stage.fullScreen = not stage.fullScreen;
              }
            }
            Button {
              visible: not mobile
              translateX: bind - size * 3.25
              onMouseClicked: function(event) {
                if (stage.fullScreen) {
                  stage.fullScreen = false
                }
                stage.iconified = true
              }
            }
          ]
        }
        Group {
          blocksMouse: true
          translateX: bind border
          translateY: bind border + offset
          content: bind content
          /*TODO:clip: Rectangle {
            width: bind width
            height: bind height
          }*/
        }
      ]
    }
  }
}
class Button extends CustomNode {
  override function create() {
    Group {
      def rectangle = Rectangle {
        visible: bind rotate != 0
        x: bind - size / 16
        y: bind - size / 4
        width: bind size / 8
        height: bind size / 2
        fill: bind foreground
      }
      opacity: bind if (hover or mobile) 1.0 else 0.3
      content: [
        Circle {
        
          radius: bind size / 2
          fill: bind foreground
        }
        Circle {
          radius: bind size * 7 / 16
          fill: bind background
        }
        Rectangle {
          x: bind rectangle.y
          y: bind rectangle.x
          width: bind rectangle.height
          height: bind rectangle.width
          fill: bind foreground
        }
        rectangle
      ]
    }
  }
}