content: /**
   * Copyright 2020 Alexander Herzog
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package ui.modeleditor;
  
  import java.awt.FlowLayout;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.stream.Collectors;
  
  import javax.swing.JCheckBox;
  import javax.swing.JComboBox;
  import javax.swing.JPanel;
  
  import language.Language;
  
  /**
   * Diese Klasse stellt einen Eintrag auf einem
   * mglichen temporren Pfad dar.
   * @author Alexander Herzog
   * @see PathPoint
   */
  public class PathPointEntry {
  	/**
  	 * Ausgangsstation
  	 */
  	private final PathPoint point;
  
  	/**
  	 * Mgliche Zwischenstationen
  	 */
  	private final List<PathPoint> options;
  
  	/**
  	 * Panel in dem Auswahlbox und Checkbox angezeigt werden sollen
  	 * @see #getPanel()
  	 */
  	private final JPanel panel;
  
  	/** Auswahlfeld fr die mglichen nchsten Stationen */
  	private final JComboBox<String> combo;
  	/** Option: Gegenrichtung auch verwenden */
  	private final JCheckBox check;
  
  	/**
  	 * Liste der Listener, die ber nderungen benachrichtigt werden sollen
  	 */
  	private final List<Runnable> changeListeners;
  
  	/**
  	 * Handelt es sich um eine interne nderung von Daten, die keine Aktionen auslsen soll?
  	 */
  	private boolean isInternalChange;
  
  	/**
  	 * Konstruktor der Klasse
  	 * @param point	Ausgangsstation
  	 * @param options	Mgliche Zwischenstationen
  	 * @param select	Zielstation
  	 */
  	public PathPointEntry(final PathPoint point, final List<PathPoint> options, final PathPoint select) {
  		this.point=point;
  		this.options=options;
  		isInternalChange=false;
  
  		panel=new JPanel(new FlowLayout(FlowLayout.LEFT));
  
  		combo=new JComboBox<>(getNextStationNames());
  		if (select!=null && options.contains(select)) {
  			combo.setSelectedIndex(1+options.indexOf(select));
  		} else {
  			combo.setSelectedIndex(0);
  		}
  		combo.addActionListener(e->selectionChanged());
  
  		check=new JCheckBox(Language.tr("PathEditor.InverseDirectionToo"),false);
  		check.setEnabled(combo.getSelectedIndex()>0);
  		check.addActionListener(e->checkChanged());
  
  		panel.add(combo);
  		panel.add(check);
  
  		changeListeners=new ArrayList<>();
  	}
  
  	/**
  	 * Konstruktor der Klasse
  	 * @param point	Ausgangsstation
  	 * @param options	Mgliche Zwischenstationen
  	 */
  	public PathPointEntry(final PathPoint point, final List<PathPoint> options) {
  		this(point,options,null);
  	}
  
  	/**
  	 * Liefert die Namen der mglichen Folgestationen.
  	 * @return	Liste der Namen der mglichen Folgestationen
  	 * @see #combo
  	 */
  	private String[] getNextStationNames() {
  		final List<String> list=options.stream().map(p->p.getLongName()).collect(Collectors.toList());
  		list.add(0,Language.tr("PathEditor.NoSelection"));
  		return list.toArray(String[]::new);
  	}
  
  	/**
  	 * Wird aufgerufen, wenn sich die Auswahl gendert hat,
  	 * um die weiteren GUI-Elemente zu aktualisieren.
  	 * @see #combo
  	 */
  	private void selectionChanged() {
  		for (PathPoint option: options) option.setNextNamesListCheckBoxes();
  		check.setEnabled(combo.getSelectedIndex()>0);
  		testReverse();
  		fireChangeListeners();
  	}
  
  	/**
  	 * Wird aufgerufen, wenn die Checkbox, ob die Gegenrichtung
  	 * auch entsprechend einbezogen werden soll, gendert wurde.
  	 * @see #check
  	 */
  	private void checkChanged() {
  		final int index=combo.getSelectedIndex();
  		if (index<1) return;
  		final PathPoint connection=options.get(index-1);
  		connection.setConnectedTo(point,check.isSelected());
  	}
  
  	/**
  	 * Liefert die als nchsten Pfadschritt gewhlte Station
  	 * @return	Als nchsten Pfadschritt gewhlte Station (kann <code>null</code> sein)
  	 */
  	public PathPoint getSelected() {
  		final int index=combo.getSelectedIndex();
  		if (index<1) return null;
  		return options.get(index-1);
  	}
  
  	/**
  	 * Stellt die Station fr den nchsten Pfadschritt ein
  	 * @param point	Station fr den nchsten Pfadschritt ein
  	 */
  	public void setSelected(final PathPoint point) {
  		int index=-1;
  		if (point!=null) index=options.indexOf(point);
  		if (index<0) index=0;
  
  		boolean isInternal=isInternalChange;
  		isInternalChange=true;
  		try {
  			combo.setSelectedIndex(index+1);
  			check.setEnabled(combo.getSelectedIndex()>0);
  			if (index>=0) check.setSelected(true);
  		} finally {
  			isInternalChange=isInternal;
  		}
  	}
  
  	/**
  	 * Liefert die Einstellungen als Panel.
  	 * @return	Einstellungen als Panel
  	 */
  	public JPanel getPanel() {
  		return panel;
  	}
  
  	/**
  	 * Fgt einen Listener zu der Liste der bei nderungen zu benachrichtigenden Listener hinzu.
  	 * @param changeListener	Zustzlicher zu benachrichtigender Listener
  	 */
  	public void addChangeListener(final Runnable changeListener) {
  		if (!changeListeners.contains(changeListener)) changeListeners.add(changeListener);
  	}
  
  	/**
  	 * Entfernt einen Listener aus der Liste der bei nderungen zu benachrichtigenden Listener.
  	 * @param changeListener	Nicht mehr zu benachrichtigender Listener
  	 * @return	Gibt <code>true</code> zurck, wenn der Listener aus der Liste entfernt werden konnte
  	 */
  	public boolean removeChangeListener(final Runnable changeListener) {
  		return changeListeners.remove(changeListener);
  	}
  
  	/**
  	 * Benachrichtigt alle Listener, die ber nderungen benachrichtigt werden sollen.
  	 * @see #changeListeners
  	 */
  	private void fireChangeListeners() {
  		for (Runnable listener: changeListeners) listener.run();
  	}
  
  	/**
  	 * Passt die Verbindung in die umgekehrte Richtung an.
  	 */
  	public void testReverse() {
  		boolean isInternal=isInternalChange;
  		isInternalChange=true;
  		try {
  			if (combo.getSelectedIndex()<1) {
  				check.setEnabled(false);
  				check.setSelected(false);
  				return;
  			}
  			check.setEnabled(true);
  			check.setSelected(options.get(combo.getSelectedIndex()-1).isConnectedTo(point));
  		} finally {
  			isInternalChange=isInternal;
  		}
  	}
  }