/*
 * Open Source Physics software is free software as described near the bottom of this code file.
 *
 * For additional information and documentation on Open Source Physics please see:
 * <https://www.compadre.org/osp/>
 */

package org.opensourcephysics.display3d.core;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import org.opensourcephysics.display3d.core.interaction.InteractionEvent;
import org.opensourcephysics.display3d.core.interaction.InteractionListener;
import org.opensourcephysics.tools.ToolsRes;

/**
 * <p>Title: CameraInspector</p>
 * <p>Description: This class creates an inspector for the camera.
 * The inspector is provided in a javax.swing.JPanel, so that
 * it can be used in other applications. A convenience class
 * is  provided to create a JFrame that contains the inspector.
 * </p>
 *
 * @author Francisco Esquembre
 * @version July 2005
 * @see Camera
 */
public class CameraInspector extends JPanel implements InteractionListener {
  private DrawingPanel3D panel = null;
  private Camera camera = null;
  private NumberFormat format = new java.text.DecimalFormat("0.000"); //$NON-NLS-1$
  private JTextField xField, yField, zField;
  private JTextField focusxField, focusyField, focuszField;
  private JTextField azimuthField, altitudeField;
  private JTextField rotationField, distanceField;
  private JRadioButton perspectiveRB, noperspectiveRB, planarxyRB, planarxzRB, planaryzRB;
  private java.util.AbstractList<ActionListener> listeners = new java.util.ArrayList<ActionListener>();

  /**
   * Creates a frame and, inside it, a CameraInspector.
   * @param panel DrawingPanel3D The drawing panel 3D with the inspected camera
   * @return JFrame
   */
  static public JFrame createFrame(DrawingPanel3D panel) {
    return new CameraInspectorFrame(ToolsRes.getString("CameraInspector.FrameTitle"), new CameraInspector(panel)); //$NON-NLS-1$
  }

  /**
   * Creates a frame with the given CameraInspector.
   * @param inspector CameraInspector The inspector provided
   * @return JFrame
   */
  static public JFrame createFrame(CameraInspector inspector) {
    return new CameraInspectorFrame(ToolsRes.getString("CameraInspector.FrameTitle"), inspector); //$NON-NLS-1$
  }

  /**
   * Creates a JPanel with a CameraInspector
   * @param panel DrawingPanel3D The drawing panel 3D with the inspected camera
   */
  public CameraInspector(DrawingPanel3D panel) {
    this.panel = panel;
    this.camera = panel.getCamera();
    panel.addInteractionListener(this);
    ActionListener fieldListener = new ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        String cmd = evt.getActionCommand();
        JTextField field = (JTextField) evt.getSource();
        double value = 0.0;
        try {
          value = format.parse(field.getText()).doubleValue();
        } catch(java.text.ParseException exc) {
          value = 0.0;
        }
        if(cmd.equals("x")) {                                                                                   //$NON-NLS-1$
          camera.setXYZ(value, camera.getY(), camera.getZ());
        } else if(cmd.equals("y")) {                                                                            //$NON-NLS-1$
          camera.setXYZ(camera.getX(), value, camera.getZ());
        } else if(cmd.equals("z")) {                                                                            //$NON-NLS-1$
          camera.setXYZ(camera.getX(), camera.getY(), value);
        } else if(cmd.equals("focusx")) {                                                                       //$NON-NLS-1$
          camera.setFocusXYZ(value, camera.getFocusY(), camera.getFocusZ());
        } else if(cmd.equals("focusy")) {                                                                       //$NON-NLS-1$
          camera.setFocusXYZ(camera.getFocusX(), value, camera.getFocusZ());
        } else if(cmd.equals("focusz")) {                                                                       //$NON-NLS-1$
          camera.setFocusXYZ(camera.getFocusX(), camera.getFocusY(), value);
        } else if(cmd.equals("azimuth")) {                                                                      //$NON-NLS-1$
          camera.setAzimuth(value);
        } else if(cmd.equals("altitude")) {                                                                     //$NON-NLS-1$
          camera.setAltitude(value);
        } else if(cmd.equals("rotation")) {                                                                     //$NON-NLS-1$
          camera.setRotation(value);
        } else if(cmd.equals("screen")) {                                                                       //$NON-NLS-1$
          camera.setDistanceToScreen(value);
        }
        CameraInspector.this.panel.repaint();
        updateFields();
        ActionEvent event = new ActionEvent(CameraInspector.this, ActionEvent.ACTION_PERFORMED, "FieldChange"); //$NON-NLS-1$
        for(java.util.Iterator<ActionListener> it = listeners.iterator(); it.hasNext(); ) {
          (it.next()).actionPerformed(event);
        }
      }

    };
    ActionListener buttonListener = new ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        String cmd = evt.getActionCommand();
        if(cmd.equals("reset")) {                                                                                //$NON-NLS-1$
          camera.reset();
          CameraInspector.this.panel.repaint();
          updateFields();
        } else if(cmd.equals("perspective")) {                                                                   //$NON-NLS-1$
          camera.setProjectionMode(Camera.MODE_PERSPECTIVE);
        } else if(cmd.equals("perspective_on")) {                                                                //$NON-NLS-1$
          camera.setProjectionMode(Camera.MODE_PERSPECTIVE_ON);
        } else if(cmd.equals("no_perspective")) {                                                                //$NON-NLS-1$
          camera.setProjectionMode(Camera.MODE_NO_PERSPECTIVE);
        } else if(cmd.equals("perspective_off")) {                                                               //$NON-NLS-1$
          camera.setProjectionMode(Camera.MODE_PERSPECTIVE_OFF);
        } else if(cmd.equals("planarXY")) {                                                                      //$NON-NLS-1$
          camera.setProjectionMode(Camera.MODE_PLANAR_XY);
        } else if(cmd.equals("planarXZ")) {                                                                      //$NON-NLS-1$
          camera.setProjectionMode(Camera.MODE_PLANAR_XZ);
        } else if(cmd.equals("planarYZ")) {                                                                      //$NON-NLS-1$
          camera.setProjectionMode(Camera.MODE_PLANAR_YZ);
        }
        camera.reset();
        ActionEvent event = new ActionEvent(CameraInspector.this, ActionEvent.ACTION_PERFORMED, "ButtonChange"); //$NON-NLS-1$
        for(java.util.Iterator<ActionListener> it = listeners.iterator(); it.hasNext(); ) {
          (it.next()).actionPerformed(event);
        }
      }

    };
    setLayout(new BorderLayout());
    JPanel projectionPanel = new JPanel(new GridLayout(2, 3));
    projectionPanel.setBorder(new TitledBorder(ToolsRes.getString("CameraInspector.ProjectionMode"))); //$NON-NLS-1$
    ButtonGroup group = new ButtonGroup();
    perspectiveRB = new JRadioButton(ToolsRes.getString("CameraInspector.Perspective")); //$NON-NLS-1$
    perspectiveRB.setActionCommand("perspective"); //$NON-NLS-1$
    perspectiveRB.addActionListener(buttonListener);
    projectionPanel.add(perspectiveRB);
    group.add(perspectiveRB);
    planarxyRB = new JRadioButton(ToolsRes.getString("CameraInspector.PlanarXY")); //$NON-NLS-1$
    planarxyRB.setActionCommand("planarXY"); //$NON-NLS-1$
    planarxyRB.addActionListener(buttonListener);
    projectionPanel.add(planarxyRB);
    group.add(planarxyRB);
    planaryzRB = new JRadioButton(ToolsRes.getString("CameraInspector.PlanarYZ")); //$NON-NLS-1$
    planaryzRB.setActionCommand("planarYZ"); //$NON-NLS-1$
    planaryzRB.addActionListener(buttonListener);
    projectionPanel.add(planaryzRB);
    group.add(planaryzRB);
    noperspectiveRB = new JRadioButton(ToolsRes.getString("CameraInspector.NoPerspective")); //$NON-NLS-1$
    noperspectiveRB.setActionCommand("no_perspective"); //$NON-NLS-1$
    noperspectiveRB.addActionListener(buttonListener);
    projectionPanel.add(noperspectiveRB);
    group.add(noperspectiveRB);
    planarxzRB = new JRadioButton(ToolsRes.getString("CameraInspector.PlanarXZ")); //$NON-NLS-1$
    planarxzRB.setActionCommand("planarXZ"); //$NON-NLS-1$
    planarxzRB.addActionListener(buttonListener);
    projectionPanel.add(planarxzRB);
    group.add(planarxzRB);
    JPanel topPanel = new JPanel(new BorderLayout());
    topPanel.add(projectionPanel, BorderLayout.CENTER);
    add(projectionPanel, BorderLayout.NORTH);
    JPanel labelPanel = new JPanel(new GridLayout(0, 1));
    JPanel fieldPanel = new JPanel(new GridLayout(0, 1));
    JPanel label2Panel = new JPanel(new GridLayout(0, 1));
    JPanel field2Panel = new JPanel(new GridLayout(0, 1));
    xField = createRow(labelPanel, fieldPanel, "X", fieldListener);                 //$NON-NLS-1$
    yField = createRow(labelPanel, fieldPanel, "Y", fieldListener);                 //$NON-NLS-1$
    zField = createRow(labelPanel, fieldPanel, "Z", fieldListener);                 //$NON-NLS-1$
    focusxField = createRow(label2Panel, field2Panel, "FocusX", fieldListener);     //$NON-NLS-1$
    focusyField = createRow(label2Panel, field2Panel, "FocusY", fieldListener);     //$NON-NLS-1$
    focuszField = createRow(label2Panel, field2Panel, "FocusZ", fieldListener);     //$NON-NLS-1$
    azimuthField = createRow(labelPanel, fieldPanel, "Azimuth", fieldListener);     //$NON-NLS-1$
    altitudeField = createRow(labelPanel, fieldPanel, "Altitude", fieldListener);   //$NON-NLS-1$
    // createRow (label2Panel, field2Panel, null,fieldListener); // emptyrow
    rotationField = createRow(label2Panel, field2Panel, "Rotation", fieldListener); //$NON-NLS-1$
    distanceField = createRow(label2Panel, field2Panel, "Screen", fieldListener);   //$NON-NLS-1$
    // createRow (label2Panel, field2Panel, null,fieldListener); // emptyrow
    JPanel leftPanel = new JPanel(new BorderLayout());
    leftPanel.add(labelPanel, BorderLayout.WEST);
    leftPanel.add(fieldPanel, BorderLayout.CENTER);
    JPanel rightPanel = new JPanel(new BorderLayout());
    rightPanel.add(label2Panel, BorderLayout.WEST);
    rightPanel.add(field2Panel, BorderLayout.CENTER);
    JPanel centerPanel = new JPanel(new GridLayout(1, 0));
    centerPanel.setBorder(new TitledBorder(ToolsRes.getString("CameraInspector.CameraParameters"))); //$NON-NLS-1$
    centerPanel.add(leftPanel);
    centerPanel.add(rightPanel);
    add(centerPanel, BorderLayout.CENTER);
    JButton resetButton = new JButton(ToolsRes.getString("CameraInspector.ResetCamera")); //$NON-NLS-1$
    resetButton.setActionCommand("reset"); //$NON-NLS-1$
    resetButton.addActionListener(buttonListener);
    add(resetButton, BorderLayout.SOUTH);
    updateFields();
  }

  /**
   * Sets the format for the fields in the inspector
   * @param format NumberFormat
   */
  public void setFormat(NumberFormat format) {
    this.format = format;
  }

  /**
   * Adds a listener to any change in the camera settings
   * @param defaultFormat NumberFormat
   */
  public void addActionListener(ActionListener listener) {
    listeners.add(listener);
  }

  /**
   * Removes a listener to any change in the camera settings
   * @param defaultFormat NumberFormat
   */
  public void removeActionListener(ActionListener listener) {
    listeners.remove(listener);
  }

  /**
   * Public as result of the implementation. Not to be used directly by final users.
   * @param _event InteractionEvent
   */
  public void interactionPerformed(InteractionEvent _event) {
    if(_event.getSource()!=panel) {
      return;
    }
    if(_event.getInfo()!=null) {
      return; // It is not changing the camera
    }
    updateFields();
  }

  public void updateFields() {
    switch(camera.getProjectionMode()) {
       default :
       case Camera.MODE_PERSPECTIVE :
       case Camera.MODE_PERSPECTIVE_ON :
         perspectiveRB.setSelected(true);
         break;
       case Camera.MODE_NO_PERSPECTIVE :
       case Camera.MODE_PERSPECTIVE_OFF :
         noperspectiveRB.setSelected(true);
         break;
       case Camera.MODE_PLANAR_XY :
         planarxyRB.setSelected(true);
         break;
       case Camera.MODE_PLANAR_XZ :
         planarxzRB.setSelected(true);
         break;
       case Camera.MODE_PLANAR_YZ :
         planaryzRB.setSelected(true);
         break;
    }
    xField.setText(format.format(camera.getX()));
    yField.setText(format.format(camera.getY()));
    zField.setText(format.format(camera.getZ()));
    focusxField.setText(format.format(camera.getFocusX()));
    focusyField.setText(format.format(camera.getFocusY()));
    focuszField.setText(format.format(camera.getFocusZ()));
    azimuthField.setText(format.format(camera.getAzimuth()));
    altitudeField.setText(format.format(camera.getAltitude()));
    rotationField.setText(format.format(camera.getRotation()));
    distanceField.setText(format.format(camera.getDistanceToScreen()));
  }

  static private JTextField createRow(JPanel labelParent, JPanel fieldParent, String labelText, ActionListener listener) {
    if(labelText==null) { // create an empty row
      labelParent.add(new JLabel());
      fieldParent.add(new JLabel());
      return null;
    }
    //    if(labelText.length()<14) {}
    JLabel label = new JLabel(ToolsRes.getString("CameraInspector."+labelText)); //$NON-NLS-1$
    label.setHorizontalAlignment(SwingConstants.CENTER);
    label.setBorder(new EmptyBorder(0, 3, 0, 3));
    JTextField field = new JTextField(4);
    field.setActionCommand(labelText.toLowerCase());
    field.addActionListener(listener);
    labelParent.add(label);
    fieldParent.add(field);
    return field;
  }

}

class CameraInspectorFrame extends JFrame {
  private CameraInspector inspector;

  /**
   * Constructor CameraInspectorFrame
   * @param title
   * @param anInspector
   */
  public CameraInspectorFrame(String title, CameraInspector anInspector) {
    super(title);
    inspector = anInspector;
    getContentPane().setLayout(new BorderLayout());
    getContentPane().add(inspector, BorderLayout.CENTER);
    pack();
  }

  public void setVisible(boolean vis) {
    super.setVisible(vis);
    if(vis) {
      inspector.updateFields();
    }
  }

}

/*
 * Open Source Physics software is free software; you can redistribute
 * it and/or modify it under the terms of the GNU General Public License (GPL) as
 * published by the Free Software Foundation; either version 2 of the License,
 * or(at your option) any later version.

 * Code that uses any portion of the code in the org.opensourcephysics package
 * or any subpackage (subdirectory) of this package must must also be be released
 * under the GNU GPL license.
 *
 * This software 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; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
 * or view the license online at http://www.gnu.org/copyleft/gpl.html
 *
 * Copyright (c) 2019  The Open Source Physics project
 *                     https://www.compadre.org/osp
 */