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 simulator.builder;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.List;
  import java.util.stream.Collectors;
  
  import language.Language;
  import mathtools.NumberTools;
  import simulator.editmodel.EditModel;
  import simulator.runmodel.RunModelFixer;
  import ui.modeleditor.ModelResource;
  import ui.modeleditor.ModelResources;
  import ui.modeleditor.ModelSurface;
  import ui.modeleditor.ModelSurface.TimeBase;
  import ui.modeleditor.coreelements.ModelElement;
  import ui.modeleditor.coreelements.ModelElementBox;
  import ui.modeleditor.coreelements.ModelElementPosition;
  import ui.modeleditor.elements.ModelElementAnalogValue;
  import ui.modeleditor.elements.ModelElementBatch;
  import ui.modeleditor.elements.ModelElementConveyor;
  import ui.modeleditor.elements.ModelElementCounter;
  import ui.modeleditor.elements.ModelElementCounterMulti;
  import ui.modeleditor.elements.ModelElementInteractiveSlider;
  import ui.modeleditor.elements.ModelElementOutputDDE;
  import ui.modeleditor.elements.ModelElementProcess;
  import ui.modeleditor.elements.ModelElementSectionEnd;
  import ui.modeleditor.elements.ModelElementSectionStart;
  import ui.modeleditor.elements.ModelElementSource;
  import ui.modeleditor.elements.ModelElementSourceMulti;
  import ui.modeleditor.elements.ModelElementSourceRecord;
  import ui.modeleditor.elements.ModelElementSplit;
  import ui.modeleditor.elements.ModelElementStateStatistics;
  import ui.modeleditor.elements.ModelElementSub;
  import ui.modeleditor.elements.ModelElementTank;
  import ui.modeleditor.elements.ModelElementTeleportDestination;
  import ui.modeleditor.elements.ModelElementTeleportSource;
  import ui.modeleditor.elements.ModelElementTransportParking;
  import ui.modeleditor.elements.ModelElementTransportTransporterSource;
  
  /**
   * Diese Klasse enthlt die statischen Hilfsroutinen, die es ermglichen
   * bestimmte Korrekturen an dem Modell vorzunehmen.
   * @author Alexander Herzog
   * @see RunModelCreatorStatus
   * @see RunModelFixer
   */
  public class RunModelFixerHelper {
  	/**
  	 * Konstruktor der Klasse<br>
  	 * Diese Klasse kann nicht instanziert werden.
  	 * Sie stellt nur statische Hilfsfunktionen bereit
  	 */
  	private RunModelFixerHelper() {}
  
  	/**
  	 * Prft, ob ein Name fr eine Station bereits genutzt wird.
  	 * @param surface	Zeichenflche deren Stationen (und ggf. Stationen von Unterelementen) geprft werden sollen
  	 * @param name	Zu prfender Name
  	 * @return	Liefert <code>false</code>, wenn der Name noch <em>nicht</em> verwendet wird
  	 */
  	private static boolean nameInUse(final ModelSurface surface, final String name) {
  		for (ModelElement element: surface.getElements()) {
  			if (element.getName().equalsIgnoreCase(name)) return true;
  			if (element instanceof ModelElementSub) {
  				if (nameInUse(((ModelElementSub)element).getSubSurface(),name)) return true;
  			}
  		}
  		return false;
  	}
  
  	/**
  	 * Erstellt basierend auf einem Namensanfang einen eindeutigen Namen (der im Modell noch nicht verwendet wird) zur Verfgung
  	 * @param model	Editor-Modell aus dem die bereits verwendeten Namen ausgelesen werden sollen
  	 * @param baseName	Basisname
  	 * @return	Neuer Name, der auf dem Basisnamen basiert und noch nicht in dem Modell vorkommt
  	 */
  	private static String makeIndividualName(final EditModel model, final String baseName) {
  		int count=1;
  		while (true) {
  			String name=(count==1)?baseName:(baseName+count);
  			if (!nameInUse(model.surface,name)) return name;
  			count++;
  		}
  	}
  
  	/**
  	 * Korrektur fr "Ein notwendige Elementenname fehlt."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#NO_NAME
  	 */
  	public static List<RunModelFixer> setName(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		if (!(element instanceof ModelElementBox)) return new ArrayList<>();
  		final String name=makeIndividualName(element.getModel(),((ModelElementBox)element).getTypeName());
  		return Arrays.asList(new RunModelFixer(
  				element,
  				status,
  				String.format(Language.tr("Surface.PopupMenu.QuickFix.General.Name"),name),
  				fix->element.setName(name)
  				));
  	}
  
  	/**
  	 * Korrektur fr "Ein notwendiger Gruppenname fehlt."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#NO_GROUP_NAME
  	 */
  	public static List<RunModelFixer> setGroupName(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		if (element instanceof ModelElementCounter) {
  			final ModelElementCounter counter=(ModelElementCounter)element;
  			final String name=Language.tr("Surface.Counter.DefaultCounterName");
  			return Arrays.asList(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.General.SetGroupName"),name),
  					fix->counter.setGroupName(name)
  					));
  		}
  
  		if (element instanceof ModelElementCounterMulti) {
  			final ModelElementCounterMulti counterMulti=(ModelElementCounterMulti)element;
  			final String name=Language.tr("Surface.Counter.DefaultCounterName");
  			return Arrays.asList(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.General.SetGroupName"),name),
  					fix->counterMulti.setGroupName(name)
  					));
  		}
  
  		if (element instanceof ModelElementStateStatistics) {
  			final ModelElementStateStatistics statistics=(ModelElementStateStatistics)element;
  			final String name=Language.tr("Surface.StateStatistics.DefaultGroupName");
  			return Arrays.asList(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.General.SetGroupName"),name),
  					fix->statistics.setGroupName(name)
  					));
  		}
  
  		return new ArrayList<>();
  	}
  
  	/**
  	 * Liefert alle Kundenquell-Datenstze aus einem Element
  	 * @param element	Element dem alle Kundenquell-Datenstze entnommen werden sollen
  	 * @return	Listem it allen verfgbaren Kundenquell-Datenstzen (kann <code>null</code> sein, wenn das Element keine Daten enthlt)
  	 */
  	private static List<ModelElementSourceRecord> getSourceRecord(final ModelElementPosition element) {
  		if (element instanceof ModelElementSource)  {
  			return Arrays.asList(((ModelElementSource)element).getRecord());
  		}
  		if (element instanceof ModelElementSourceMulti)  {
  			return ((ModelElementSourceMulti)element).getRecords().stream().filter(record->record.isActive()).collect(Collectors.toList());
  		}
  		if (element instanceof ModelElementSplit) {
  			return ((ModelElementSplit)element).getRecords().stream().filter(record->record.isActive()).collect(Collectors.toList());
  		}
  
  		return null;
  	}
  
  	/**
  	 * Liefert eine Liste mit allen Kundentypennamen
  	 * @param model	Editor-Modell aus dem die Kundentypennamen ausgelesen werden sollen
  	 * @return	Liste mit allen Kundentypennamen
  	 */
  	private static List<String> getClientTypeNames(final EditModel model) {
  		final List<String> list=model.surface.getClientTypes();
  		if (list!=null && !list.isEmpty()) return list;
  		return Arrays.asList(Language.tr("Surface.Source.DefaultGroupName"));
  	}
  
  	/**
  	 * Korrektur fr "Ein notwendiger Kundendatensatz-Name fehlt."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#NO_SOURCE_RECORD_NAME
  	 */
  	public static List<RunModelFixer> setSourceRecordName(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		final List<ModelElementSourceRecord> records=getSourceRecord(element);
  		final List<String> suggestedNames=getClientTypeNames(element.getModel());
  		if (records!=null && !records.isEmpty()) {
  			if (!records.get(0).hasName())  {
  				/* Name der Station selbst ist leer */
  				for (String name: suggestedNames) {
  					options.add(new RunModelFixer(
  							element,
  							status,
  							String.format(Language.tr("Surface.PopupMenu.QuickFix.SourceRecord.StationName"),name),
  							fix->element.setName(name)
  							));
  				}
  			} else {
  				/* Name eines Datensatzes ist leer */
  				for (int i=0;i<records.size();i++) {
  					final ModelElementSourceRecord record=records.get(i);
  					if (record.getName().isEmpty()) for (String name: suggestedNames) {
  						options.add(new RunModelFixer(
  								element,
  								status,
  								String.format(Language.tr("Surface.PopupMenu.QuickFix.SourceRecord.RecordName"),i+1,name),
  								fix->record.setName(name)
  								));
  					}
  				}
  			}
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Es wurde ein negativer Zeitpunkt fr den Start der Planung der ersten Kundenankunft gewhlt."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#NEGATIVE_ARRIVAL_START_TIME
  	 */
  	public static List<RunModelFixer> setArrivalStartTime(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		final List<ModelElementSourceRecord> records=getSourceRecord(element);
  		if (records!=null) for (int i=0;i<records.size();i++) {
  			final ModelElementSourceRecord record=records.get(i);
  			final ModelElementSourceRecord.NextMode mode=record.getNextMode();
  			if (mode==ModelElementSourceRecord.NextMode.NEXT_DISTRIBUTION || mode==ModelElementSourceRecord.NextMode.NEXT_EXPRESSION || mode==ModelElementSourceRecord.NextMode.NEXT_CONDITION) {
  				if (record.getArrivalStart()<0) {
  					if (records.size()>1) {
  						/* Mehrere Datenstze */
  						options.add(new RunModelFixer(
  								element,
  								status,
  								String.format(Language.tr("Surface.PopupMenu.QuickFix.SourceRecord.ArrivalStart.Multi"),i+1),
  								fix->record.setArrivalStart(0)
  								));
  					} else {
  						/* Nur ein Datensatz (nicht geklrt ob nur einer mglich oder nur einer vorhanden - aber auch nicht relevant) */
  						options.add(new RunModelFixer(
  								element,
  								status,
  								String.format(Language.tr("Surface.PopupMenu.QuickFix.SourceRecord.ArrivalStart.Single")),
  								fix->record.setArrivalStart(0)
  								));
  					}
  				}
  			}
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Bei einem Analogwert-nderungs-Benachrichtigung ist der zeitliche Abstand der Prfungen negativ."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#ANALOG_NOTIFY_NEGATIVE
  	 */
  	public static List<RunModelFixer> setAnalogNotifyTime(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		final double value=60.0;
  		if (element instanceof ModelElementAnalogValue) {
  			final ModelElementAnalogValue analog=(ModelElementAnalogValue)element;
  			options.add(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.Analog.NotifyValue"),NumberTools.formatNumber(value)),
  					fix->analog.setAnalogNotify(value)
  					));
  		}
  		if (element instanceof ModelElementTank) {
  			final ModelElementTank analog=(ModelElementTank)element;
  			options.add(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.Analog.NotifyValue"),NumberTools.formatNumber(value)),
  					fix->analog.setAnalogNotify(value)
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Initialer Wert fr Analog-Wert-Element niedriger als angegebener Minimalwert."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#ANALOG_INITIAL_LOWER_THAN_MIN
  	 */
  	public static List<RunModelFixer> setAnalogInitialHigher(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementAnalogValue) {
  			final ModelElementAnalogValue analog=(ModelElementAnalogValue)element;
  			final double value=analog.getValueMin();
  			options.add(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.Analog.SetInitialValue"),NumberTools.formatNumber(value)),
  					fix->analog.setInitialValue(value)
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Initialer Wert fr Analog-Wert-Element hher als angegebener Maximalwert."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#ANALOG_INITIAL_HIGHER_THAN_MAX
  	 */
  	public static List<RunModelFixer> setAnalogInitialLower(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementAnalogValue) {
  			final ModelElementAnalogValue analog=(ModelElementAnalogValue)element;
  			final double value=analog.getValueMax();
  			options.add(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.Analog.SetInitialValue"),NumberTools.formatNumber(value)),
  					fix->analog.setInitialValue(value)
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Maximalwert fr Analog-Wert-Element niedriger als angegebener Minimalwert."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#ANALOG_MAX_LOWER_THAN_MIN
  	 */
  	public static List<RunModelFixer> setAnalogMinMax(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementAnalogValue) {
  			final ModelElementAnalogValue analog=(ModelElementAnalogValue)element;
  			final double value=analog.getValueMin()+10;
  			options.add(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.Analog.SetMaxValue"),NumberTools.formatNumber(value)),
  					fix->analog.setValueMax(value)
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Die Batch-Gre an einer Batch-Station ist kleiner als 1."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#FIXED_BATCH_SIZE_LOWER_THAN_1
  	 */
  	public static List<RunModelFixer> setBatchFixedError(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementBatch) {
  			final ModelElementBatch batch=(ModelElementBatch)element;
  			options.add(new RunModelFixer(
  					element,
  					status,
  					Language.tr("Surface.PopupMenu.QuickFix.Batch.SetFixed1"),
  					fix->batch.getBatchRecord().setBatchSizeFixed("1")
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Die minimale Batch-Gre an einer Batch-Station ist kleiner als 1."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#MIN_BATCH_SIZE_LOWER_THAN_1
  	 */
  	public static List<RunModelFixer> setBatchMinError(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementBatch) {
  			final ModelElementBatch batch=(ModelElementBatch)element;
  			options.add(new RunModelFixer(
  					element,
  					status,
  					Language.tr("Surface.PopupMenu.QuickFix.Batch.SetMin1"),
  					fix->batch.getBatchRecord().setBatchSizeMin("1")
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Die maximale Batch-Gre an einer Batch-Station ist kleiner als die minimale Batch-Gre."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#MAX_BATCH_SIZE_LOWER_THAN_MIN
  	 */
  	public static List<RunModelFixer> setBatchMaxError(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementBatch) {
  			final ModelElementBatch batch=(ModelElementBatch)element;
  			final String value=batch.getBatchRecord().getBatchSizeMin();
  			options.add(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.Batch.SetMaxMin"),value),
  					fix->batch.getBatchRecord().setBatchSizeMax(value)
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Es wurde eine negative Fliebandkapazitt angegeben."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#CONVEYOR_CAPACITY_NEGATIVE
  	 */
  	public static List<RunModelFixer> setConveyorCapacity(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementConveyor) {
  			final ModelElementConveyor conveyor=(ModelElementConveyor)element;
  			options.add(new RunModelFixer(
  					element,
  					status,
  					Language.tr("Surface.PopupMenu.QuickFix.Conveyor.Capacity"),
  					fix->conveyor.setCapacityAvailable(1)
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Es wurde eine negative Zeitdauer fr den Transport auf einem Flieband angegeben."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#CONVEYOR_TIME_NEGATIVE
  	 */
  	public static List<RunModelFixer> setConveyorTime(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementConveyor) {
  			final ModelElementConveyor conveyor=(ModelElementConveyor)element;
  			final double value=60.0;
  			options.add(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.Conveyor.Time"),value),
  					fix->{conveyor.setTransportTime(value); conveyor.setTimeBase(TimeBase.TIMEBASE_SECONDS);}
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Bei einem Slider ist ein kleinerer Maximalwert als der Minimalwert angegeben."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#SLIDER_MAX_LOWER_THAN_MIN
  	 */
  	public static List<RunModelFixer> setSliderMinMax(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementInteractiveSlider) {
  			final ModelElementInteractiveSlider slider=(ModelElementInteractiveSlider)element;
  			final double value=slider.getMinValue()+10;
  			options.add(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.Slider.MaxValue"),NumberTools.formatNumber(value)),
  					fix->slider.setMaxValue(value)
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Bei einem Slider ist keine Schrittweite grer als 0 angegeben."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#SLIDER_STEP_LESS_OR_EQUAL_0
  	 */
  	public static List<RunModelFixer> setSliderStep(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementInteractiveSlider) {
  			final ModelElementInteractiveSlider slider=(ModelElementInteractiveSlider)element;
  			final double value=1;
  			options.add(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.Slider.Step"),NumberTools.formatNumber(value)),
  					fix->slider.setStep(value)
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Liefert eine Liste mit allen Stationsnamen auf einer Zeichenflche (und mglicher Unterzeichenflchen)
  	 * @param surface	Zeichenflche deren Stationsnamen aufgelistet werden sollen
  	 * @return	Liste mit allen Stationsnamen
  	 */
  	private static List<String> getSectionNames(final ModelSurface surface) {
  		final List<String> names=new ArrayList<>();
  		for (ModelElement element: surface.getElements()) {
  			if (element instanceof ModelElementSectionStart && !element.getName().isBlank()) names.add(element.getName());
  			if (element instanceof ModelElementSub) names.addAll(getSectionNames(((ModelElementSub)element).getSubSurface()));
  		}
  		return names;
  	}
  
  	/**
  	 * Korrektur fr "Bei einer Station zum Verlassen eines Bereichs ist kein Name fr den Bereich, der verlassen werden soll, angegeben."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#NO_SECTION_NAME
  	 */
  	public static List<RunModelFixer> setSectionEndSection(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementSectionEnd) {
  			final ModelElementSectionEnd sectionEnd=(ModelElementSectionEnd)element;
  			final List<String> names=getSectionNames(element.getModel().surface);
  			for (String name: names) {
  				options.add(new RunModelFixer(
  						element,
  						status,
  						String.format(Language.tr("Surface.PopupMenu.QuickFix.SectionEnd.Section"),name),
  						fix->sectionEnd.setSectionStartName(name)
  						));
  			}
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Es wurde eine ungltige Kapazitt (kleiner oder gleich 0) fr einen Tank angegeben."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#TANK_CAPACITY_LESS_OR_EQUAL_0
  	 */
  	public static List<RunModelFixer> setTankCapacity(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementTank) {
  			final ModelElementTank tank=(ModelElementTank)element;
  			final double value=100.0;
  			options.add(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.Tank.Capacity"),NumberTools.formatNumber(value)),
  					fix->tank.setCapacity(value)
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Es wurde ein ungltiger Startfllstand (kleiner als das Minimum oder grer als das Maximum) fr einen Tank angegeben."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#TANK_INVALID_INITIAL_VALUE
  	 */
  	public static List<RunModelFixer> setTankInitialValue(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementTank) {
  			final ModelElementTank tank=(ModelElementTank)element;
  			final double oldInitial=tank.getInitialValue();
  			final double max=tank.getCapacity();
  			if (oldInitial<0) {
  				final double value=0.0;
  				options.add(new RunModelFixer(
  						element,
  						status,
  						String.format(Language.tr("Surface.PopupMenu.QuickFix.Tank.Initial"),NumberTools.formatNumber(value)),
  						fix->tank.setInitialValue(value)
  						));
  			} else {
  				if (oldInitial>max) {
  					final double value=max;
  					options.add(new RunModelFixer(
  							element,
  							status,
  							String.format(Language.tr("Surface.PopupMenu.QuickFix.Tank.Initial"),NumberTools.formatNumber(value)),
  							fix->tank.setInitialValue(value)
  							));
  				}
  			}
  		}
  		return options;
  	}
  
  	/**
  	 * Liefert eine Liste mit allen mglichen Transportzielen einer Zeichenflche  (und mglicher Unterzeichenflchen)
  	 * @param surface	Zeichenflche deren Transportziele aufgelistet werden sollen
  	 * @return	Liste mit allen mglichen Transportzielen
  	 */
  	private static List<String> getTeleportDestinations(final ModelSurface surface) {
  		final List<String> names=new ArrayList<>();
  		for (ModelElement element: surface.getElements()) {
  			if (element instanceof ModelElementTeleportDestination && !element.getName().isBlank()) names.add(element.getName());
  			if (element instanceof ModelElementSub) names.addAll(getSectionNames(((ModelElementSub)element).getSubSurface()));
  		}
  		return names;
  	}
  
  	/**
  	 * Korrektur fr "Die fr einen Teleport-Transport angegebene Zielstation ist ungltig."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#TELEPORT_INVALID_DESTINATION
  	 */
  	public static List<RunModelFixer> setTeleportDestination(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementTeleportSource) {
  			final ModelElementTeleportSource teleport=(ModelElementTeleportSource)element;
  			final List<String> names=getTeleportDestinations(element.getModel().surface);
  			for (String name: names) {
  				options.add(new RunModelFixer(
  						element,
  						status,
  						String.format(Language.tr("Surface.PopupMenu.QuickFix.Teleport.DestinationName"),name),
  						fix->teleport.setDestination(name)
  						));
  			}
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Der Parkplatz besitzt eine negative Kapazitt."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#PARKING_NEGATIVE_CAPACITY
  	 */
  	public static List<RunModelFixer> setParkingCapacity(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementTransportParking) {
  			final ModelElementTransportParking parking=(ModelElementTransportParking)element;
  			final int value=1;
  			options.add(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.Transporter.Parking"),value),
  					fix->parking.setWaitingCapacity(value)
  					));
  		}
  		return options;
  	}
  
  
  	/**
  	 * Korrektur fr "Ein Transporter-Startpunkt besitzt eine negative Parkplatzkapazitt."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#TRANSPORTER_SOURCE_NEGATIVE_PARKING_CAPACITY
  	 */
  	public static List<RunModelFixer> setTransporterParkingCapacity(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementTransportTransporterSource) {
  			final ModelElementTransportTransporterSource tranporterSource=(ModelElementTransportTransporterSource)element;
  			final int value=0;
  			options.add(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.Transporter.Parking"),value),
  					fix->tranporterSource.setWaitingCapacity(value)
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Ein Transporter-Startpunkt besitzt nicht-positive Mindestanzahl an wartenden Kunden um einen Transporter anzufordern."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#TRANSPORTER_SOURCE_MIN_REQUEST_NUMBER_LESS_OR_EQUAL_0
  	 */
  	public static List<RunModelFixer> setTransporterMinRequest(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementTransportTransporterSource) {
  			final ModelElementTransportTransporterSource tranporterSource=(ModelElementTransportTransporterSource)element;
  			final int value=1;
  			options.add(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.Transporter.MinRequest"),value),
  					fix->tranporterSource.setRequestMinWaiting(value)
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Ungltige Startzeile fr die DDE-Ausgabe."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#DDE_OUTPUT_INVALID_ROW
  	 */
  	public static List<RunModelFixer> setDDEOutputRow(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementOutputDDE) {
  			final ModelElementOutputDDE output=(ModelElementOutputDDE)element;
  			options.add(new RunModelFixer(
  					element,
  					status,
  					Language.tr("Surface.PopupMenu.QuickFix.OutputDDE.Row"),
  					fix->output.setStartRow(1)
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Ungltige Spalte fr die DDE-Ausgabe."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#DDE_OUTPUT_INVALID_COL
  	 */
  	public static List<RunModelFixer> setDDEOutputCol(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementOutputDDE) {
  			final ModelElementOutputDDE output=(ModelElementOutputDDE)element;
  			options.add(new RunModelFixer(
  					element,
  					status,
  					Language.tr("Surface.PopupMenu.QuickFix.OutputDDE.Col"),
  					fix->output.setColumn("A")
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Es wurde an einer Bedienstation eine Wartezeittoleranz angegeben, aber es wurde keine auslaufende Kante fr die Warteabbrecher angegeben."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#PROCESS_CANCELATION_TIME_BUT_NO_EDGE
  	 */
  	public static List<RunModelFixer> processTurnOffCancelTimes(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementProcess) {
  			final ModelElementProcess process=(ModelElementProcess)element;
  			options.add(new RunModelFixer(
  					element,
  					status,
  					Language.tr("Surface.PopupMenu.QuickFix.Process.DeactivateLimitedWaitingTimeTolerances"),
  					fix->process.getCancel().set(null)
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Es wurden keine notwendigen Ressourcen an der Bedienstation angegeben."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#PROCESS_NO_RESOURCE
  	 */
  	public static List<RunModelFixer> processResources(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementProcess) {
  			final ModelElementProcess process=(ModelElementProcess)element;
  			final ModelResources resources=process.getSurface().getResources();
  
  			/* Neue Gruppe anlegen */
  			final String newGroup=resources.getNextAvailableResouceName();
  			options.add(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.Process.Resource.New"),newGroup),
  					fix->{
  						resources.add(new ModelResource(newGroup,1));
  						process.getNeededResources().get(0).put(newGroup,1);
  					}
  					));
  
  			/* Bestehende Gruppe verwenden */
  			for (ModelResource resource: resources.getResources()) {
  				options.add(new RunModelFixer(
  						element,
  						status,
  						String.format(Language.tr("Surface.PopupMenu.QuickFix.Process.Resource.Use"),resource.getName()),
  						fix->process.getNeededResources().get(0).put(resource.getName(),1)
  						));
  			}
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "Es wurden an der Bedienstation Batch-Bedienungen und Rstzeiten gleichzeitig verwendet, was nicht zulssig ist."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#PROCESS_MIX_BATCH_AND_SETUP
  	 */
  	public static List<RunModelFixer> processBatchSetup(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementProcess) {
  			final ModelElementProcess process=(ModelElementProcess)element;
  			options.add(new RunModelFixer(
  					element,
  					status,
  					Language.tr("Surface.PopupMenu.QuickFix.Process.SetupOff"),
  					fix->process.getSetupTimes().clear()
  					));
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "An einer Bedienstation wurde eine minimale Bedien-Batchgre von weniger als 1 angegeben."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#PROCESS_MIN_BATCH_LOWER_THAN_1
  	 */
  	public static List<RunModelFixer> processMinBatchSize(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementProcess) {
  			final ModelElementProcess process=(ModelElementProcess)element;
  			for (int i=1;i<=5;i++) {
  				final int size=i;
  				options.add(new RunModelFixer(
  						element,
  						status,
  						String.format(Language.tr("Surface.PopupMenu.QuickFix.Process.MinBatchSize"),size),
  						fix->process.setBatchMinimum(size)
  						));
  			}
  		}
  		return options;
  	}
  
  	/**
  	 * Korrektur fr "An einer Bedienstation wurde eine maximale Bedien-Batchgre angegeben, die geringer ist als die minimale Bedien-Batchgre."
  	 * @param element	Element an dem der Fehler aufgetreten ist
  	 * @param status	Zugehriger Status fr diesen Fehler
  	 * @return	Liste der mglichen Schnellkorrekturen
  	 * @see RunModelCreatorStatus.Status#PROCESS_MAX_BATCH_LOWER_THAN_MIN
  	 */
  	public static List<RunModelFixer> processMaxBatchSize(final ModelElementPosition element, final RunModelCreatorStatus status) {
  		final List<RunModelFixer> options=new ArrayList<>();
  		if (element instanceof ModelElementProcess) {
  			final ModelElementProcess process=(ModelElementProcess)element;
  			final int size=process.getBatchMinimum();
  			options.add(new RunModelFixer(
  					element,
  					status,
  					String.format(Language.tr("Surface.PopupMenu.QuickFix.Process.MaxBatchSize"),size),
  					fix->process.setBatchMaximum(size)
  					));
  		}
  		return options;
  	}
  }
  