/*
 * 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.
 */
package simulation;

import java.awt.AWTEvent;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.Timer;

import ilog.views.IlvManagerView;
import ilog.views.IlvManagerViewInteractor;
import ilog.views.IlvPoint;
import ilog.views.IlvTransformer;
import ilog.views.event.InteractorChangedEvent;
import ilog.views.event.InteractorListener;
import ilog.views.interactor.IlvPermanentInteractorInterface;
import ilog.views.maps.IlvCoordinate;
import ilog.views.maps.IlvCoordinateSystemProperty;
import ilog.views.maps.defense.symbology.app6a.IlvApp6aSymbol;
import ilog.views.maps.srs.coordsys.IlvCoordinateSystem;
import ilog.views.maps.srs.coordsys.IlvGeographicCoordinateSystem;
import ilog.views.maps.srs.coordtrans.IlvCoordinateTransformation;
import ilog.views.maps.srs.coordtrans.IlvCoordinateTransformationException;
import ilog.views.sdm.IlvSDMEngine;
import ilog.views.sdm.event.SDMModelEvent;
import ilog.views.sdm.event.SDMModelListener;
import ilog.views.sdm.renderer.IlvStyleSheetRenderer;
import ilog.views.util.IlvImageUtil;

/**
 * Illustrates how to log events on the symbology and to replay scenarios.
 */
public class SimulationController extends JToolBar {
  final static String NEUTRAL = "Neutral".intern(); //$NON-NLS-1$
  final static String FOLLOW = "Follow".intern(); //$NON-NLS-1$
  private final IlvSDMEngine symbology;
  // final JCheckBox pause = new JCheckBox("Stop Creating Targets");
  // //$NON-NLS-1$
  // final JCheckBox simulation = new JCheckBox("Run simulation"); //$NON-NLS-1$
  final JButton playPauseButton = new JButton();
  final JButton stopButton = new JButton();
  final JToggleButton followSymbolButton = new JToggleButton();
  private boolean isRunning = false;
  private boolean needInit = true;
  final JComboBox<Integer> mobileCount = new JComboBox<Integer>(new Integer[] { Integer.valueOf(10), Integer.valueOf(50),
      Integer.valueOf(100), Integer.valueOf(300), Integer.valueOf(1000), Integer.valueOf(1500) });
  final JComboBox<String> speedCombo = new JComboBox<String>(
      new String[] { "x10", "x25", "x50", "x75", "x100", "x250" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
  final JComboBox<Integer> fpsCombo = new JComboBox<Integer>(
      new Integer[] { Integer.valueOf(5), Integer.valueOf(10), Integer.valueOf(25), Integer.valueOf(50) });
  SimulationContext simulationContext;
  Icon playIcon;
  Icon pauseIcon;
  Icon stopIcon;
  Icon followSymbolIcon;
  FollowSymbolInteractor followInteractor = new FollowSymbolInteractor();

  /**
   * Create the replay interface.
   * 
   * @param symbology
   */
  public SimulationController(IlvSDMEngine symbology) {
    createUserInterface();
    this.symbology = symbology;
    playPauseButton.addActionListener(new SimulationBoxListener());
    SimulationBoxListener listener = new SimulationBoxListener();
    stopButton.addActionListener(listener);
    followSymbolButton.addActionListener(listener);
    // JV-2856
    InteractorListener interactorListener = new InteractorListener() {
      Override
      public void interactorChanged(InteractorChangedEvent event) {
        boolean isMyInteractor = (event.getNewValue() == followInteractor);
        if (followSymbolButton.isSelected() != isMyInteractor) {
          followSymbolButton.setSelected(isMyInteractor);
        }
      }
    };
    symbology.getReferenceView().addInteractorListener(interactorListener);

  }

  private void createUserInterface() {
    speedCombo.setSelectedIndex(0);
    speedCombo.setToolTipText("Choose simulation speed factor"); //$NON-NLS-1$ (should
                                                                 // be
                                                                 // internationalized)
    SimulationContext.setTimeAcceleration(10);
    speedCombo.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        int accel = Integer.parseInt(((String) speedCombo.getSelectedItem()).substring(1));
        SimulationContext.setTimeAcceleration(accel);
      }
    });
    fpsCombo.setSelectedIndex(0);
    fpsCombo.setToolTipText("Choose target refresh rate for the display"); //$NON-NLS-1$ (should
                                                                           // be
                                                                           // internationalized)
    SimulationContext.setTargetFPS(((Integer) fpsCombo.getSelectedItem()).intValue());
    fpsCombo.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        SimulationContext.setTargetFPS(((Integer) fpsCombo.getSelectedItem()).intValue());
      }
    });
    Dimension buttonDim = new Dimension(26, 26);
    Dimension comboDim = new Dimension(55, 22);
    configureComponentSize(playPauseButton, buttonDim);
    playPauseButton.setSelected(false);
    add(playPauseButton);
    configureComponentSize(stopButton, buttonDim);
    add(stopButton);
    configureComponentSize(followSymbolButton, buttonDim);
    add(followSymbolButton);
    // setLayout(new FlowLayout(FlowLayout.LEFT,2,0));
    addSeparator();
    add(new JLabel("Mobiles ")); //$NON-NLS-1$ (should be internationalized)
    // addSeparator();
    mobileCount.setToolTipText("Choose number of mobiles"); //$NON-NLS-1$ (should
                                                            // be
                                                            // internationalized)
    configureComponentSize(mobileCount, comboDim);
    add(mobileCount);
    addSeparator();
    add(new JLabel("Speed ")); //$NON-NLS-1$ (should be internationalized)
    // addSeparator();
    configureComponentSize(speedCombo, comboDim);
    add(speedCombo);
    addSeparator();
    add(new JLabel("Fps ")); //$NON-NLS-1$ (should be internationalized)
    // addSeparator();
    configureComponentSize(fpsCombo, comboDim);
    add(fpsCombo);
    mobileCount.setEnabled(true);
    // install icons
    playIcon = createIcon("right.gif"); //$NON-NLS-1$
    pauseIcon = createIcon("pause.gif"); //$NON-NLS-1$
    stopIcon = createIcon("stop.gif"); //$NON-NLS-1$
    // stopTargetCreationIcon = createIcon("stopTargetCreation.gif");
    // //$NON-NLS-1$
    // targetCreationIcon = createIcon("targetCreation.gif"); //$NON-NLS-1$
    followSymbolIcon = createIcon("target.gif"); //$NON-NLS-1$
    playPauseButton.setIcon(playIcon);
    playPauseButton.setToolTipText("Run simulation"); //$NON-NLS-1$ (should be
                                                      // internationalized)
    stopButton.setIcon(stopIcon);
    stopButton.setToolTipText("Stop simulation"); //$NON-NLS-1$ (should be
                                                  // internationalized)
    followSymbolButton.setIcon(followSymbolIcon);
    followSymbolButton.setToolTipText("Choose a symbol to follow"); //$NON-NLS-1$ (should
                                                                    // be
                                                                    // internationalized)
  }

  private void configureComponentSize(JComponent comp, Dimension buttonDim) {
    comp.setPreferredSize(buttonDim);
    comp.setMinimumSize(buttonDim);
    comp.setMaximumSize(buttonDim);
  }

  private Icon createIcon(String iconURL) {
    Image image = null;
    try {
      image = IlvImageUtil.getImageFromFile(SimulationController.class, iconURL);
    } catch (IOException e1) {
      e1.printStackTrace();
    }
    if (image != null) {
      return new ImageIcon(image);
    }
    return null;
  }

  IlvApp6aSymbol dca;

  private final class SimulationBoxListener implements ActionListener {
    /**
     * Called when the button "Run simulation" is pressed.
     * 
     * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
     */
    Override
    public void actionPerformed(ActionEvent e) {
      if (e.getSource() == playPauseButton) {
        isRunning = !isRunning;
        if (isRunning) {
          if (needInit) {
            Container c = playPauseButton.getTopLevelAncestor();
            Cursor oc = null;
            if (c.isCursorSet()) {
              oc = c.getCursor();
            }
            c.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            buildSimulation();
            // set triple buffering
            IlvStyleSheetRenderer renderer = IlvStyleSheetRenderer.getInstance(symbology);
            symbology.getReferenceView().setTripleBufferedLayerCount(renderer.getNodesLayer());
            targetProvider.start();
            mobileCount.setEnabled(false);
            needInit = false;
            c.setCursor(oc);
          } else {
            // resume simulation
            if (targetProvider != null)
              // targetProvider.start();
              Mobile.resume();
          }
          playPauseButton.setToolTipText("Pause simulation"); //$NON-NLS-1$ (should
                                                              // be
                                                              // internationalized)
          playPauseButton.setIcon(pauseIcon);
        } else {
          playPauseButton.setToolTipText("Run simulation"); //$NON-NLS-1$ (should
                                                            // be
                                                            // internationalized)
          playPauseButton.setIcon(playIcon);
          // pause simulation
          isRunning = false;
          Mobile.pause();
        }
      } else if (e.getSource() == stopButton) {
        Mobile.resume();
        isRunning = false;
        stopSimu();
        // stop triple buffering
        symbology.getReferenceView().setTripleBufferedLayerCount(0);
        mobileCount.setEnabled(true);
        playPauseButton.setToolTipText("Run simulation"); //$NON-NLS-1$ (should
                                                          // be
                                                          // internationalized)
        playPauseButton.setIcon(playIcon);
        playPauseButton.setEnabled(true);
        Enumeration<?> roots = symbology.getModel().getObjects();
        Vector<Object> v = new Vector<Object>();
        while (roots.hasMoreElements()) {
          v.add(roots.nextElement());
        }
        for (int i = 0; i < v.size(); i++) {
          symbology.getModel().removeObject(v.get(i));
        }
        Mobile.allMobiles.clear();
        Mobile.currentMobileTask = null;
        needInit = true;
      } else if (e.getSource() == followSymbolButton) {
        IlvManagerView view = symbology.getReferenceView();
        if (view != null) {
          view.requestFocus();
          if (view.getInteractor() == followInteractor) {
            view.popInteractor();
          } else {
            view.pushInteractor(followInteractor);
          }
        }
      }
    }

    void stopSimu() {
      if (targetProvider != null)
        // targetProvider.stop();
        Mobile.stop();
    }

    void buildSimulation() {
      SimulationContext.setSeed(124);
      dca = new IlvApp6aSymbol("SFGPUCFHS--CUS*", 0, 0);//$NON-NLS-1$
      double lon = (SimulationContext.getLonMin() + SimulationContext.getLonMax()) / 2;
      double lat = (SimulationContext.getLatMin() + SimulationContext.getLatMax()) / 2;
      dca.setLocation(symbology, lon, lat);
      symbology.getModel().addObject(dca, null, null);
      int neutralMobilesCount = ((Integer) (mobileCount.getSelectedItem())).intValue();
      // neutral mobiles.
      for (int i = 0; i < neutralMobilesCount; i++) {
        /* Target t= */new Target(NEUTRAL, symbology);
      }
      targetProvider.setInitialDelay(100);
      double accel = SimulationContext.getTimeAcceleration();
      targetProvider.setDelay((int) (50000 / accel));
      // targetProvider.start();
      Mobile.start();
    }
  }

  Timer targetProvider = new Timer(20000, new ActionListener() {
    Override
    public void actionPerformed(ActionEvent e) {
      if (!Mobile.paused && isRunning == true) {
        Target t = new Target(symbology);
        t.symbol.setProperty(IlvApp6aSymbol.ID_CODE, "SHAPMHA---FA--A"); //$NON-NLS-1$
        new Missile(dca.getLongitude(), dca.getLatitude(), symbology, t);
        double accel = SimulationContext.getTimeAcceleration();
        int newDelay = (int) (50000 / accel);
        if (targetProvider.getDelay() != newDelay) {
          targetProvider.setDelay((int) (50000 / accel));
        }
      }
    }
  });
  final static java.text.DecimalFormat NS = new java.text.DecimalFormat("0 mph"); //$NON-NLS-1$
  final static java.text.DecimalFormat FS = new java.text.DecimalFormat("0 ft"); //$NON-NLS-1$

  /**
   * @author eaullas
   */
  private class FollowSymbolInteractor extends IlvManagerViewInteractor implements IlvPermanentInteractorInterface {
    /** To save cursor. */
    Cursor oldCursor = null;
    /** The cursor for this interactor. */
    Cursor myCursor = null;

    /**
     * Creates a new <code>FollowSymbolInteractor</code>.
     */
    public FollowSymbolInteractor() {
      super();
      enableEvents(AWTEvent.MOUSE_EVENT_MASK);
      myCursor = new Cursor(Cursor.CROSSHAIR_CURSOR);
    }

    Override
    public boolean isPermanent() {
      return false;
    }

    Override
    public void setPermanent(boolean permanent) {
      // ignore this.permanent = permanent;
    }

    /**
     * This method is called when the interactor is attached to the specified
     * manager view.
     * 
     * @param v
     *          view to attach to.
     */
    Override
    protected void attach(IlvManagerView v) {
      super.attach(v);
      if (!getCursor().equals(v.getCursor())) {
        oldCursor = v.getCursor();
        v.setCursor(getCursor());
      }
    }

    /**
     * This method is called when the interactor is detached from the specified
     * manager view.
     */
    Override
    protected void detach() {
      if (oldCursor != null) {
        getManagerView().setCursor(oldCursor);
        oldCursor = null;
      }
      if (modelListener != null) {
        symbology.getModel().removeSDMModelListener(modelListener);
        modelListener = null;
      }
      modelListener = null;
      follow(null);
      super.detach();
    }

    /**
     * @return Returns the cursor used for edition.
     */
    public Cursor getCursor() {
      if (myCursor == null) {
        if (getManagerView() != null)
          return getManagerView().getCursor();
        return Cursor.getDefaultCursor();
      }
      return myCursor;
    }

    Override
    protected void processMouseEvent(MouseEvent e) {
      switch (e.getID()) {
      case MouseEvent.MOUSE_RELEASED:
        Object dataObject = symbology.getObject(new IlvPoint(e.getX(), e.getY()), getManagerView());
        // if(e.getClickCount()>1){
        // if(dataObject != null){
        // symbPanel.editSelectedSymbol();
        // }
        // }
        if (dataObject instanceof IlvApp6aSymbol) {
          follow((IlvApp6aSymbol) dataObject);
        } else {
          follow(null);
        }
        break;
      }
      super.processMouseEvent(e);
    }

    IlvApp6aSymbol currentSymbol;
    SDMModelListener modelListener;

    void follow(IlvApp6aSymbol symbol) {
      if (currentSymbol != null) {
        symbology.getModel().setObjectProperty(currentSymbol, FOLLOW, "0"); //$NON-NLS-1$
      }
      currentSymbol = symbol;
      if (currentSymbol != null) {
        symbology.getModel().setObjectProperty(currentSymbol, FOLLOW, "1");//$NON-NLS-1$
      }
      updateForCurrentSymbol();
      if (modelListener == null) {
        modelListener = new SDMModelListener() {
          Override
          public void adjustmentFinished(SDMModelEvent event) {
            if (currentSymbol != null) {
              updateForCurrentSymbol();
            }
          }

          Override
          public void dataChanged(SDMModelEvent event) {
            if (event.getObject() == currentSymbol) {
              updateForCurrentSymbol();
            }
          }

          Override
          public void linkDestinationChanged(SDMModelEvent event) {/* ignore */
          }

          Override
          public void linkSourceChanged(SDMModelEvent event) {/* ignore */
          }

          Override
          public void objectAdded(SDMModelEvent event) {/* ignore */
          }

          Override
          public void objectRemoved(SDMModelEvent event) {
            if (event.getObject() == currentSymbol) {
              currentSymbol = null;
              follow(null);
            }
          }
        };
        symbology.getModel().addSDMModelListener(modelListener);
      }
    }

    void updateForCurrentSymbol() {
      Object dir = null;
      if (currentSymbol != null) {
        dir = currentSymbol.getProperty(IlvApp6aSymbol.MODIFIER_DIRECTION_OF_MOVEMENT_INDICATOR);
      }
      double direction = -90;
      if (dir != null) {
        direction = Double.parseDouble(dir.toString());
      }
      IlvManagerView view = getManagerView();
      IlvCoordinateSystem system = IlvCoordinateSystemProperty.GetCoordinateSystem(view.getManager());
      IlvCoordinateTransformation tr = IlvCoordinateTransformation
          .CreateTransformation(IlvGeographicCoordinateSystem.KERNEL, system);
      IlvTransformer t = view.getTransformer();
      if (currentSymbol != null) {
        IlvCoordinate c1 = new IlvCoordinate(currentSymbol.getLongitude(), -currentSymbol.getLatitude());
        if (tr != null) {
          try {
            tr.transform(c1, c1);
          } catch (IlvCoordinateTransformationException e) {
            e.printStackTrace();
          }
        }
        IlvPoint c = new IlvPoint((float) c1.x, (float) c1.y);
        t.apply(c);
        t.translate(view.getWidth() / 2 - c.x, view.getHeight() / 2 - c.y);
      }
      double angle = Math.toDegrees(Math.atan2(t.getx21(), t.getx11()));
      double dangle = -90 - direction - angle;
      t.rotate(view.getWidth() / 2, view.getHeight() / 2, dangle);
      // make sure that an almost 0 rotation is exactly 0 (performance is
      // better)
      if (Math.abs(t.getx12()) < 1E-4 && Math.abs(t.getx21()) < 1E-4) {// RAZ
                                                                       // Rotation
                                                                       // when
                                                                       // it is
                                                                       // very
                                                                       // small.
        t.setValues(t.getx11(), 0, 0, t.getx22());
      }
      view.setTransformer(t);
      view.invalidateView();
      view.repaint();
    }
  }
}