package demo.view.realizer;

import demo.view.DemoBase;
import y.geom.YDimension;
import y.geom.YRectangle;
import y.view.EditMode;
import y.view.PopupMode;
import y.view.NodeRealizer;
import y.view.NodeLabel;
import y.view.Graph2DViewActions;
import y.base.Node;
import y.view.SizeConstraintProvider;

import javax.swing.JPopupMenu;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.Action;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;

/**
 * Demonstrates how to achieve automatic node size adjustment according to the node's label.
 */
public class AutoNodeBoundsDemo extends DemoBase {
  private AutoNodeBoundsDemo.LabelChangeListener listener;

  public AutoNodeBoundsDemo() {
  }

  LabelChangeListener getLabelChangeListener(){
    if(listener == null){
      listener = new LabelChangeListener();
    }
    return listener;
  }

  protected void registerViewActions() {
    super.registerViewActions();
    //use default predefined actions but hand over our own listener to the EDIT_LABEL action.
    // So the node size will also be adjusted when using the default "edit label" keybinding "F2"
    ActionMap map = view.getCanvasComponent().getActionMap();
    Action action = map.get(Graph2DViewActions.EDIT_LABEL);
    action.putValue("PROPERTY_CHANGE_LISTENER", getLabelChangeListener());
  }

  protected void registerViewModes() {
    //add a popup view mode that listens to the right mouse click
    //and displays context sensitive menus
    EditMode mode = new EditMode(){

      protected void nodeCreated(Node v) {
        super.nodeCreated(v);

        //always open the label editor when creating a node
        NodeLabel label = getGraph2D().getRealizer(v).getLabel();
        view.openLabelEditor(label,
          label.getLocation().getX(),
          label.getLocation().getY(),
          getLabelChangeListener()
        );
      }
    };
    mode.setPopupMode( new AutoNodeBoundsDemo.DemoPopupMode() );
    view.addViewMode( mode );
  }

  class DemoPopupMode extends PopupMode {
    /**
     * Popup menu for a hit node
     */
    public JPopupMenu getNodePopup( Node v ) {
      JPopupMenu pm = new JPopupMenu();
      NodeRealizer r = this.view.getGraph2D().getRealizer( v );
      pm.add( new AutoNodeBoundsDemo.EditNodeLabel( r.getLabel()) );
      return pm;
    }
  }
  /**
   * Action that opens a text editor for the label of a node
   */
  class EditNodeLabel extends AbstractAction {
    private NodeLabel label;

    EditNodeLabel(NodeLabel label) {
      super( "Edit Label" );
      this.label = label;
    }

    public void actionPerformed( ActionEvent e ) {
      view.openLabelEditor( label,
          label.getLocation().getX(),
          label.getLocation().getY(),
          getLabelChangeListener()
      );
    }
  }

  class LabelChangeListener implements PropertyChangeListener{
    private double minWidth = 40.0;
    private double minHeight = 40.0;
    private boolean ignoreWidth = false;
    private boolean ignoreHeight = false;
    private double hSpace = 2.0;
    private double vSpace = 2.0;

    public void propertyChange(PropertyChangeEvent evt) {
      NodeLabel label = (NodeLabel) evt.getSource();
      adjustNodeSize(label.getRealizer());
    }

    void adjustNodeSize(NodeRealizer nr) {
      if (nr.labelCount() < 1) {
        return;
      }

      double minWidth = this.minWidth;
      double minHeight = this.minHeight;
      double maxWidth = Double.POSITIVE_INFINITY;
      double maxHeight = Double.POSITIVE_INFINITY;
      final SizeConstraintProvider scp = nr.getSizeConstraintProvider();
      if (scp != null) {
        final YDimension min = scp.getMinimumSize();
        if (min != null) {
          if (minWidth < min.width) {
            minWidth = min.width;
          }
          if (minHeight < min.height) {
            minHeight = min.height;
          }
        }
        final YDimension max = scp.getMaximumSize();
        if (max != null) {
          maxWidth = max.width;
          maxHeight = max.height;
        }
      }

      final NodeLabel nl = nr.getLabel();
      final YRectangle lb = nl.getBox();

      if (!ignoreWidth) {
        double width = lb.width + 2 * hSpace;
        if (width < minWidth) {
          width = minWidth;
        }
        if (width > maxWidth) {
          width = maxWidth;
        }
        nr.setWidth(width);
      }
      if (!ignoreHeight) {
        double height = lb.height + 2 * vSpace;
        if (height < minHeight) {
          height = minHeight;
        }
        if (height > maxHeight) {
          height = maxHeight;
        }
        nr.setHeight(height);
      }
    }
  }

  public static void main( String[] args ) {
    EventQueue.invokeLater(new Runnable() {
      public void run() {
        initLnF();
        (new AutoNodeBoundsDemo()).start("Auto Node Bounds Demo");
      }
    });
  }
}
