/** Updates the dialog with the value of the current route */
  private void initValues() {

    // Should not trigger listeners
    quiescent = true;

    nameTxT.setText(route.getName());
    originTxT.setText(route.getDeparture());
    destinationTxT.setText(route.getDestination());

    etaCalculationTime.setSelectedItem(route.getEtaCalculationType());

    // Update the route start time and the start time-related fields
    adjustStartTime();

    cbVisible.setSelected(route.isVisible());

    if (route.getWaypoints().size() > 1) {
      allSpeeds.setText(Formatter.formatSpeed(route.getWaypoints().get(0).getOutLeg().getSpeed()));
    }

    updateButtonEnabledState();

    // Done
    quiescent = false;
  }
  /**
   * Handle action events
   *
   * @param evt the action event
   */
  @Override
  public void actionPerformed(ActionEvent evt) {
    // Check if we are in a quiescent state
    if (quiescent) {
      return;
    }

    if (evt.getSource() == btnZoomToRoute && chartPanel != null) {
      chartPanel.zoomToWaypoints(route.getWaypoints());

    } else if (evt.getSource() == btnZoomToWp && chartPanel != null) {
      chartPanel.goToPosition(route.getWaypoints().get(selectedWp).getPos());

    } else if (evt.getSource() == btnDelete) {
      onDelete();
      routeUpdated();

    } else if (evt.getSource() == btnActivate) {
      EPD.getInstance().getRouteManager().changeActiveWp(selectedWp);
      routeUpdated();

    } else if (evt.getSource() == btnClose) {
      dispose();

    } else if (evt.getSource() == cbVisible) {
      route.setVisible(cbVisible.isSelected());

      EPD.getInstance()
          .getRouteManager()
          .notifyListeners(RoutesUpdateEvent.ROUTE_VISIBILITY_CHANGED);

    } else if (evt.getSource() == etaCalculationTime) {
      route.setEtaCalculationType((EtaCalculationType) etaCalculationTime.getSelectedItem());
      adjustStartTime();
      routeUpdated();

    } else if (evt.getSource() == allSpeedsBtn) {
      double speed;
      try {
        speed = parseDouble(allSpeeds.getText());
        allSpeeds.setText(Formatter.formatSpeed(speed));
      } catch (FormatException e) {
        JOptionPane.showMessageDialog(
            this, "Error in speed", "Input error", JOptionPane.ERROR_MESSAGE);
        return;
      }
      for (int i = 0; i < route.getWaypoints().size(); i++) {
        RouteWaypoint wp = route.getWaypoints().get(i);
        if (wp.getOutLeg() != null && !locked[i]) {
          wp.getOutLeg().setSpeed(speed);
        }
      }
      adjustStartTime();
    }

    EPD.getInstance().getRouteManager().notifyListeners(RoutesUpdateEvent.ROUTE_CHANGED);
  }
  /**
   * Given a new arrival date, re-calculate the speed
   *
   * @param arrivalDate the new arrival date
   */
  private void recalculateSpeeds(Date arrivalDate) {
    // Stop widget listeners
    boolean wasQuiescent = quiescent;
    quiescent = true;

    // Special case if the arrival date is before the start time
    if (route.getStarttime().after(arrivalDate)) {
      // Reset arrival to a valid time
      arrivalPicker.setDate(route.getEta());
      arrivalSpinner.setValue(route.getEta());

      quiescent = wasQuiescent;
      return;
    }

    // Total distance
    Dist distanceToTravel = new Dist(DistType.NAUTICAL_MILES, route.getRouteDtg());
    // And we want to get there in milliseconds:
    Time timeToTravel =
        new Time(TimeType.MILLISECONDS, arrivalDate.getTime() - route.getStarttime().getTime());

    // Subtract the distance and time from the locked way points
    for (int i = 0; i < route.getWaypoints().size() - 1; i++) {
      if (locked[i]) {
        distanceToTravel =
            distanceToTravel.subtract(new Dist(DistType.NAUTICAL_MILES, route.getWpRng(i)));
        timeToTravel =
            timeToTravel.subtract(new Time(TimeType.MILLISECONDS, route.getWpTtg(i + 1)));
      }
    }

    // Ensure the remaining time is actually positive (say, more than a minute)
    if (timeToTravel.in(TimeType.MINUTES).doubleValue() < 1.0) {
      // Reset arrival to a valid time
      arrivalPicker.setDate(route.getEta());
      arrivalSpinner.setValue(route.getEta());

      quiescent = wasQuiescent;
      return;
    }

    // So we need to travel how fast?
    double speed = distanceToTravel.inTime(timeToTravel).in(SpeedType.KNOTS).doubleValue();

    for (int i = 0; i < route.getWaypoints().size(); i++) {
      if (!locked[i]) {
        route.getWaypoints().get(i).setSpeed(speed);
      }
    }

    // Update fields
    updateFields();

    // Restore the quiescent state
    quiescent = wasQuiescent;
  }
  /**
   * Constructor
   *
   * @param parent the parent window
   * @param route the route
   * @param readOnlyRoute whether the route is read-only or not
   */
  public RoutePropertiesDialogCommon(
      Window parent, ChartPanelCommon chartPanel, Route route, boolean readOnlyRoute) {
    super(parent, "Route Properties", Dialog.ModalityType.APPLICATION_MODAL);

    this.parent = parent;
    this.chartPanel = chartPanel;
    this.route = route;
    this.readOnlyRoute = readOnlyRoute;
    locked = new boolean[route.getWaypoints().size()];

    addWindowListener(
        new WindowAdapter() {
          @Override
          public void windowClosed(WindowEvent e) {
            EPD.getInstance()
                .getRouteManager()
                .validateMetoc(RoutePropertiesDialogCommon.this.route);
          }
        });

    initGui();
    initValues();

    setBounds(100, 100, 1000, 450);
    setLocationRelativeTo(parent);
  }
  /** Called when Delete is clicked */
  private void onDelete() {
    // Check that there is a selection
    if (selectedWp < 0) {
      return;
    }

    // If the route has two way points or less, delete the entire route
    if (route.getWaypoints().size() < 3) {
      // ... but get confirmation first
      int result =
          JOptionPane.showConfirmDialog(
              parent,
              "A route must have at least two waypoints.\nDo you want to delete the route?",
              "Delete Route?",
              JOptionPane.YES_NO_OPTION,
              JOptionPane.QUESTION_MESSAGE);
      if (result == JOptionPane.YES_OPTION) {
        EPD.getInstance()
            .getRouteManager()
            .removeRoute(EPD.getInstance().getRouteManager().getRouteIndex(route));
        EPD.getInstance().getRouteManager().notifyListeners(RoutesUpdateEvent.ROUTE_REMOVED);
        dispose();
      }
    } else {
      // Update the locked list
      boolean[] newLocked = new boolean[locked.length - 1];
      for (int x = 0; x < newLocked.length; x++) {
        newLocked[x] = locked[x + (x >= selectedWp ? 1 : 0)];
      }
      locked = newLocked;

      // Delete the selected way point
      route.deleteWaypoint(selectedWp);
      adjustStartTime();
      routeTableModel.fireTableDataChanged();
      EPD.getInstance().getRouteManager().notifyListeners(RoutesUpdateEvent.ROUTE_WAYPOINT_DELETED);
    }
  }