/*
 * Licensed Materials - Property of Perforce Software, Inc. 
 * © Copyright Perforce Software, Inc. 2014, 2021 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;

import ilog.views.IlvApplyObject;
import ilog.views.IlvGraphic;
import ilog.views.IlvManager;
import ilog.views.IlvManagerView;
import ilog.views.IlvPoint;
import ilog.views.IlvRect;
import ilog.views.IlvTransformer;
import ilog.views.graphic.IlvLine;
import ilog.views.graphic.IlvText;

/**
 * This sample shows how the alignment mechanism of rotated text works.
 * @proofread
 */
SuppressWarnings("serial")
public class RotateAlignText extends JRootPane
{
  {
    // This sample uses JViews Diagrammer features. When deploying an
    // application that includes this code, you need to be in possession
    // of a Perforce JViews Diagrammer Deployment license.
//    IlvProductUtil.DeploymentLicenseRequired(
//        IlvProductUtil.JViews_Diagrammer_Deployment);
  }

  IlvText text = 
    new IlvText(new IlvPoint(100, 100), 
                "This is a multiline\ntext that\ncan be rotated\nand aligned");
  IlvLine normLine = new IlvLine();
  IlvLine orthLine = new IlvLine();
  IlvManager manager;
  IlvManagerView view;
  int initialTextRotationAngle = 10;
  int initialThresholdAngle = 30;
  int initialRotationAngle = 45;

  /**
   * Initializes the application.
   */
  public void init()
  {
    // fill the manager with the text object and the two auxiliary lines
    manager = new IlvManager();

    text.setFont(new Font("arial", Font.BOLD, 15));
    text.setAntialiasing(true);
    text.setFractionalMetrics(true);
    manager.addObject(text, true);
    adjust(initialTextRotationAngle,
           initialRotationAngle,
           initialThresholdAngle);

    normLine.setForeground(Color.green);
    orthLine.setForeground(Color.pink);
    manager.addObject(normLine, true);
    manager.addObject(orthLine, true);

    // create the view
    view = new IlvManagerView(manager) {
      Override
      public void setBounds(int x, int y, int w, int h) {
        super.setBounds(x, y, w, h);
        centerText(w, h);
      }
    };
    view.setBackground(Color.white);
    view.setAntialiasing(true);

    // create three dial controls.

    // The outer dial controls the rotation of the text
    final JDial outerDial = new JDial(true);
    outerDial.setAngleColor(new Color(0,200,0));
    outerDial.setAngle(initialTextRotationAngle);

    // the middle dial controls the rotation alignment axes
    JDial middleDial = new JDial(false);
    middleDial.setAngleColor(new Color(0,0,200));
    middleDial.setAngle(initialRotationAngle);

    // the innermost dial controls the rotation alignment threshold angle
    JDial innerDial = new JDial(false);
    innerDial.setAngleColor(new Color(200,0,0));
    innerDial.setShowAngleColorAt4Sides(true);
    innerDial.setStartAngle(initialRotationAngle);
    innerDial.setAngle(initialThresholdAngle);

    // nest the dials and the view inside eachother
    outerDial.add(middleDial);
    middleDial.add(innerDial);
    innerDial.add(view);

    // configure the labels
    outerDial.getTopLeftLabel().setForeground(Color.green.darker());
    outerDial.getTopRightLabel().setForeground(Color.blue);
    outerDial.getBottomLeftLabel().setForeground(Color.red.darker());
    outerDial.getTopLeftLabel().setText("" + initialTextRotationAngle + " degree");
    outerDial.getTopRightLabel().setText("" + initialRotationAngle + " degree");
    outerDial.getBottomLeftLabel().setText("" + initialThresholdAngle + " degree");

    // outer dial rotates the text
    outerDial.addActionListener(new ActionListener() {
        Override
        public void actionPerformed(ActionEvent evt) {
          int angle = ((JDial)evt.getSource()).getAngle();
          outerDial.getTopLeftLabel().setText("" + angle + " degree");
          adjust(angle, text.getRotationAlignmentAngle(),
                        text.getRotationAlignmentThresholdAngle()); 
        }
      });

    // middle dial sets the rotation alignment angle
    middleDial.addActionListener(new ActionListener() {
        Override
        public void actionPerformed(ActionEvent evt) {
          JDial middleDial1 = (JDial)evt.getSource();
          int angle = middleDial1.getAngle();
          outerDial.getTopRightLabel().setText("" + angle + " degree");
          if (angle >= 180) {
            while (angle >= 180) 
              angle -= 180;
            // could indicate that the middleDial has only 180 degree range:
            // middleDial.setAngle(angle);
          }
          JDial innerDial1 = (JDial)middleDial1.getContents();
          innerDial1.setStartAngle(angle);
          adjust(getTextAngle(), angle,
                 text.getRotationAlignmentThresholdAngle());
        }
      });

    // middle dial sets the rotation alignment angle
    innerDial.addActionListener(new ActionListener() {
        Override
        public void actionPerformed(ActionEvent evt) {
          JDial innerDial1 = (JDial)evt.getSource();
          int angle = innerDial1.getAngle();
          if (angle >= 90) {
            while (angle >= 90) 
              angle -= 90;
            innerDial1.setAngle(angle);
          }
          outerDial.getBottomLeftLabel().setText("" + angle + " degree");
          adjust(getTextAngle(), text.getRotationAlignmentAngle(), angle);
        }
      });

    getContentPane().setLayout(new BorderLayout(0,0));
    getContentPane().add(outerDial, BorderLayout.CENTER);
  }

  /**
   * Adjust the text and the auxiliary lines to the new situation.
   */
  void adjust(final double textAngle,
              final double rotationAlignmentAngle,
              final double rotationAlignmentThreshold)
  {
    double currentAngle = getTextAngle();
    final double deltaAngle = textAngle - currentAngle;
    text.getGraphicBag().applyToObject(text, new IlvApplyObject() {
        Override
        public void apply(IlvGraphic g, Object arg) {
          text.rotate(new IlvPoint(), deltaAngle);
          text.setRotationAlignmentAngle(rotationAlignmentAngle);
          text.setRotationAlignmentThresholdAngle(rotationAlignmentThreshold);
        }
    }, null, true);

    // center the text object
    if (view != null) {
      Rectangle r = view.getBounds();
      centerText(r.width, r.height);
    }
  }

  /**
   * Returns the current angle of the text object.
   */
  float getTextAngle()
  {
    IlvTransformer t = text.getTransformer();
    if (t == null) return 0;
    return (float)(90 - 180 * Math.atan2(t.getx22(), t.getx21()) / Math.PI);
  }

  /**
   * Center the text object in the view.
   */
  void centerText(final int w, final int h)
  {
    // center the text
    IlvPoint p = text.getEffectiveAnchorPoint();
    IlvRect bbox = text.boundingBox();
    double dx = p.x - bbox.x;
    double dy = p.y - bbox.y;
    double x = w  / 2 - dx;
    double y = h  / 2 - dy;
    manager.moveObject(text, x, y, true);

    // rotate the two auxiliary lines as necessary
    manager.applyToObject(normLine, new IlvApplyObject() {
        Override
        public void apply(IlvGraphic g, Object arg) {
          double angle = Math.PI * text.getRotationAlignmentAngle() / 180;
          int r = Math.max(w, h);
          IlvPoint p0 = new IlvPoint(w/2 - r * Math.cos(angle),
                                     h/2 - r * Math.sin(angle));
          IlvPoint p1 = new IlvPoint(w/2 + r * Math.cos(angle),
                                     h/2 + r * Math.sin(angle));
          normLine.setFrom(p0);
          normLine.setTo(p1);
        }
     }, null, true);
    manager.applyToObject(normLine, new IlvApplyObject() {
        Override
        public void apply(IlvGraphic g, Object arg) {
          double angle = Math.PI * text.getRotationAlignmentAngle() / 180;
          int r = Math.max(w, h);
          IlvPoint p0 = new IlvPoint(w/2 - r * Math.sin(angle),
                                     h/2 + r * Math.cos(angle));
          IlvPoint p1 = new IlvPoint(w/2 + r * Math.sin(angle),
                                     h/2 - r * Math.cos(angle));
          orthLine.setFrom(p0);
          orthLine.setTo(p1);
        }
     }, null, true);
  }

  /**
   * The main routine.
   */
  public static void main(String[] args) 
  {
    // Sun recommends that to put the entire GUI initialization into the
    // AWT thread
    SwingUtilities.invokeLater(
      new Runnable() {
        Override
        public void run() {
          RotateAlignText app = new RotateAlignText();
          app.init();

          JFrame frame = new JFrame();
          frame.setTitle("JViews Rotated Aligned Text Sample");
          frame.setSize(600, 620);
          frame.getContentPane().add(app);
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.setVisible(true);
        }
      });
  }
}