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.events;
  
  import language.Language;
  import simcore.Event;
  import simcore.SimData;
  import simulator.coreelements.RunElement;
  import simulator.coreelements.RunElementData;
  import simulator.elements.RunElementSub;
  import simulator.elements.RunElementSubConnect;
  import simulator.runmodel.RunData;
  import simulator.runmodel.RunDataClient;
  import simulator.runmodel.SimulationData;
  
  /**
   * Abgang eines Kunden aus einer Station
   * @author Alexander Herzog
   */
  public class StationLeaveEvent extends Event {
  	/**
  	 * Kunde der die Station verlsst
  	 */
  	public RunDataClient client;
  
  	/**
  	 * Station die verlassen wird
  	 */
  	public RunElement station;
  
  	/**
  	 * Zhlung Kunden an Station?
  	 * @see #addLeaveEvent(SimulationData, RunDataClient, RunElement, long)
  	 * @see #addLeaveEvent(SimulationData, RunDataClient, RunElement, long, boolean)
  	 */
  	private boolean statistics;
  
  	/**
  	 * Konstruktor der Klasse
  	 */
  	public StationLeaveEvent() {
  		/*
  		 * Wird nur bentigt, um einen JavaDoc-Kommentar fr diesen (impliziten) Konstruktor
  		 * setzen zu knnen, damit der JavaDoc-Compiler keine Warnung mehr ausgibt.
  		 */
  	}
  
  	@Override
  	public void run(SimData data) {
  		final SimulationData simData=(SimulationData)data;
  
  		/* Logging */
  		if (simData.loggingActive) station.logLeave(simData,Language.tr("Simulation.Log.LeaveStation"),String.format(Language.tr("Simulation.Log.LeaveStation.Info"),client.logInfo(simData),station.name));
  
  		if (statistics) {
  			/* Zhlung Kunden an Station */
  			if (station.isClientCountStation()) simData.runData.logClientLeavesStation(simData,station,null,client);
  		}
  
  		/* Verarbeitung */
  		station.processLeave(simData,client);
  
  		/* System ber Status-nderung benachrichtigen */
  		simData.runData.fireStateChangeNotify(simData);
  	}
  
  	/**
  	 * Sendet einen Kunden an die nchste Station<br>
  	 * Es wird am Ende der Verarbeitung immer {@link RunData#fireStateChangeNotify(SimulationData)} ausgelst.
  	 * @param simData	Simulationsdatenobjekt
  	 * @param client	Kunde der an eine neue Station gesendet werden soll
  	 * @param lastStation	Ausgangsstation
  	 * @param nextStation	Nchste Station
  	 */
  	public static void sendToStation(final SimulationData simData, final RunDataClient client, final RunElement lastStation, final RunElement nextStation) {
  		sendToStation(simData,client,lastStation,nextStation,true);
  	}
  
  	/**
  	 * Sendet einen Kunden an die nchste Station
  	 * @param simData	Simulationsdatenobjekt
  	 * @param client	Kunde der an eine neue Station gesendet werden soll
  	 * @param lastStation	Ausgangsstation
  	 * @param nextStation	Nchste Station
  	 * @param fireChangedEvent	Wird hier <code>true</code> bergeben, so wird am Ende der Verarbeitung {@link RunData#fireStateChangeNotify(SimulationData)} ausgelst.
  	 */
  	public static void sendToStation(final SimulationData simData, final RunDataClient client, final RunElement lastStation, final RunElement nextStation, final boolean fireChangedEvent) {
  		client.lastStationID=(lastStation==null)?-1:lastStation.id;
  		client.nextStationID=nextStation.id;
  
  		final RunData runData=simData.runData;
  
  		/* Logging */
  		if (simData.loggingActive) nextStation.logArrive(simData,Language.tr("Simulation.Log.ArriveStation"),String.format(Language.tr("Simulation.Log.ArriveStation.Info"),client.logInfo(simData),nextStation.name));
  
  		/* Zwischenabgangszeiten in der Statistik erfassen */
  		if (lastStation!=null) {
  			runData.logStationLeave(simData.currentTime,simData,lastStation,client);
  		}
  
  		/* System ber Bewegung des Kunden benachrichtigen */
  		runData.fireClientMoveNotify(simData,client,false);
  
  		/* Zwischenankunftszeiten in der Statistik erfassen */
  		if (!(lastStation instanceof RunElementSubConnect) || !(nextStation instanceof RunElementSub)) { /* Keine Zwischenankunftszeit usw. wenn der Kunde das Submodell eigentlich gerade wieder verlsst. */
  			runData.logStationArrival(simData.currentTime,simData,nextStation,null,client);
  		}
  
  		/* Zhlung Kunden an Station */
  		if (nextStation.isClientCountStation()) runData.logClientEntersStation(simData,nextStation,null,client);
  
  		/* Verarbeitung */
  		client.iconLast=client.icon;
  		client.typeLast=client.type;
  		client.arrivalProcessedStationID=nextStation.id;
  		nextStation.processArrival(simData,client);
  
  		/* System ber Status-nderung benachrichtigen */
  		if (fireChangedEvent) runData.fireStateChangeNotify(simData);
  	}
  
  	/**
  	 * Sendet einen Kunden per Transporter-Objekt an die nchste Station
  	 * @param simData	Simulationsdatenobjekt
  	 * @param client	Kunde der an eine neue Station gesendet werden soll
  	 * @param lastStation	Ausgangsstation
  	 * @param nextStation	Nchste Station
  	 */
  	public static void sendToStationByTransporter(final SimulationData simData, final RunDataClient client, final RunElement lastStation, final RunElement nextStation) {
  		client.lastStationID=(lastStation==null)?-1:lastStation.id;
  		client.nextStationID=nextStation.id;
  
  		/* Logging */
  		if (simData.loggingActive) nextStation.logArrive(simData,Language.tr("Simulation.Log.ArriveStation"),String.format(Language.tr("Simulation.Log.ArriveStation.Info"),client.logInfo(simData),nextStation.name));
  
  		/* Zwischenabgangszeiten in der Statistik erfassen */
  		if (lastStation!=null) {
  			simData.runData.logStationLeave(simData.currentTime,simData,lastStation,client);
  		}
  
  		/* System ber Bewegung des Kunden benachrichtigen */
  		simData.runData.fireClientMoveNotify(simData,client,true);
  
  		/* Zwischenankunftszeiten in der Statistik erfassen */
  		if (!(lastStation instanceof RunElementSubConnect) || !(nextStation instanceof RunElementSub)) { /* Keine Zwischenankunftszeit usw. wenn der Kunde das Submodell eigentlich gerade wieder verlsst. */
  			simData.runData.logStationArrival(simData.currentTime,simData,nextStation,null,client);
  		}
  
  		/* Zhlung Kunden an Station */
  		if (nextStation.isClientCountStation()) simData.runData.logClientEntersStation(simData,nextStation,null,client);
  
  		/* Verarbeitung */
  		client.iconLast=client.icon;
  		client.typeLast=client.type;
  		client.arrivalProcessedStationID=nextStation.id;
  		nextStation.processArrival(simData,client);
  
  		/* System ber Status-nderung benachrichtigen */
  		simData.runData.fireStateChangeNotify(simData);
  	}
  
  	/**
  	 * Sendet mehrere Kunden an die jeweils nchsten Stationen<br>
  	 * Es wird am Ende der Verarbeitung immer {@link RunData#fireStateChangeNotify(SimulationData)} ausgelst.
  	 * @param simData	Simulationsdatenobjekt
  	 * @param client	Kunden die an eine neue Station gesendet werden sollen
  	 * @param lastStation	Ausgangsstationen
  	 * @param nextStation	Nchste Stationen
  	 */
  	public static void multiSendToStation(final SimulationData simData, final RunDataClient[] client, final RunElement lastStation, final RunElement[] nextStation) {
  
  		/* Erst Move...*/
  		for (int i=0;i<client.length;i++) {
  			client[i].lastStationID=(lastStation==null)?-1:lastStation.id;
  			client[i].nextStationID=nextStation[i].id;
  
  			/* Logging */
  			if (simData.loggingActive) nextStation[i].logArrive(simData,Language.tr("Simulation.Log.ArriveStation"),String.format(Language.tr("Simulation.Log.ArriveStation.Info"),client[i].logInfo(simData),nextStation[i].name));
  
  			/* Zwischenabgangszeiten in der Statistik erfassen */
  			if (lastStation!=null) {
  				simData.runData.logStationLeave(simData.currentTime,simData,lastStation,client[i]);
  			}
  
  			/* System ber Bewegung des Kunden benachrichtigen */
  			simData.runData.fireClientMoveNotify(simData,client[i],false);
  		}
  
  		/* ...dann Arrival */
  		for (int i=0;i<client.length;i++) {
  			/* Zwischenankunftszeiten in der Statistik erfassen */
  			simData.runData.logStationArrival(simData.currentTime,simData,nextStation[i],null,client[i]);
  
  			/* Zhlung Kunden an Station */
  			if (nextStation[i].isClientCountStation()) simData.runData.logClientEntersStation(simData,nextStation[i],null,client[i]);
  
  			/* Verarbeitung */
  			client[i].iconLast=client[i].icon;
  			client[i].typeLast=client[i].type;
  			client[i].arrivalProcessedStationID=nextStation[i].id;
  			nextStation[i].processArrival(simData,client[i]);
  		}
  
  		/* System ber Status-nderung benachrichtigen */
  		simData.runData.fireStateChangeNotify(simData);
  	}
  
  	/**
  	 * Sendet mehrere Kunden an jeweils eine gemeinsame nchste Station<br>
  	 * Es wird am Ende der Verarbeitung immer {@link RunData#fireStateChangeNotify(SimulationData)} ausgelst.
  	 * @param simData	Simulationsdatenobjekt
  	 * @param client	Kunden die an eine neue Station gesendet werden sollen
  	 * @param lastStation	Ausgangsstation
  	 * @param nextStation	Nchste Station
  	 */
  	public static void multiSendToStation(final SimulationData simData, final RunDataClient[] client, final RunElement lastStation, final RunElement nextStation) {
  
  		/* Erst Move...*/
  		for (int i=0;i<client.length;i++) {
  
  			client[i].lastStationID=(lastStation==null)?-1:lastStation.id;
  			client[i].nextStationID=nextStation.id;
  
  			/* Logging */
  			if (simData.loggingActive) nextStation.logArrive(simData,Language.tr("Simulation.Log.ArriveStation"),String.format(Language.tr("Simulation.Log.ArriveStation.Info"),client[i].logInfo(simData),nextStation.name));
  
  			/* Zwischenabgangszeiten in der Statistik erfassen */
  			if (lastStation!=null) {
  				simData.runData.logStationLeave(simData.currentTime,simData,lastStation,client[i]);
  			}
  
  			/* System ber Bewegung des Kunden benachrichtigen */
  			simData.runData.fireClientMoveNotify(simData,client[i],false);
  		}
  
  		/* ...dann Arrival */
  		for (int i=0;i<client.length;i++) {
  			/* Zwischenankunftszeiten in der Statistik erfassen */
  			simData.runData.logStationArrival(simData.currentTime,simData,nextStation,null,client[i]);
  
  			/* Zhlung Kunden an Station */
  			if (nextStation.isClientCountStation()) simData.runData.logClientEntersStation(simData,nextStation,null,client[i]);
  
  			/* Verarbeitung */
  			client[i].iconLast=client[i].icon;
  			client[i].typeLast=client[i].type;
  			client[i].arrivalProcessedStationID=nextStation.id;
  			nextStation.processArrival(simData,client[i]);
  		}
  
  		/* System ber Status-nderung benachrichtigen */
  		simData.runData.fireStateChangeNotify(simData);
  	}
  
  	/**
  	 * Legt ein {@link StationLeaveEvent}-Ereignis an.<br>
  	 * Bei der Ausfhrung des Ereignisses wird {@link RunElement#isClientCountStation()} geprft und dann ggf. {@link RunData#logClientLeavesStation(SimulationData, RunElement, RunElementData, RunDataClient)} aufgerufen.
  	 * @param simData	Simulationsdatenobjekt
  	 * @param client	Kunde der die Station verlassen soll
  	 * @param station	Station die verlassen werden soll
  	 * @param timeDelta	Zeitpunkt (in Millisekunden ab der aktuellen Zeit) an der das Ereignis ausgefhrt werden soll
  	 * @return	Liefert das erzeugte und bereits in die Ereignisliste eingefgte Ereignis zurck
  	 */
  	public static StationLeaveEvent addLeaveEvent(final SimulationData simData, final RunDataClient client, final RunElement station, final long timeDelta) {
  		/* addLeaveEvent(simData,client,station,timeDelta,true); - true = logStationLeave bei Ausfhrung des Events */
  
  		/* Station-Event-Objekt holen */
  		final StationLeaveEvent leaveStation=(StationLeaveEvent)simData.getEvent(StationLeaveEvent.class);
  
  		/* ... und initialisieren */
  		leaveStation.init(simData.currentTime+Math.max(0,timeDelta));
  
  		/* Konfiguration in Element eintragen */
  		leaveStation.station=station;
  		leaveStation.client=client;
  		leaveStation.statistics=true;
  
  		/* Zur Ereignisliste hinzufgen */
  		if (!simData.runData.stopp) simData.eventManager.addEvent(leaveStation);
  
  		return leaveStation;
  	}
  
  	/**
  	 * Legt ein {@link StationLeaveEvent}-Ereignis an.
  	 * @param simData	Simulationsdatenobjekt
  	 * @param client	Kunde der die Station verlassen soll
  	 * @param station	Station die verlassen werden soll
  	 * @param timeDelta	Zeitpunkt (in Millisekunden ab der aktuellen Zeit) an der das Ereignis ausgefhrt werden soll
  	 * @param statistics	Wird hier <code>true</code> bergeben, so wird bei der Ausfhrung des Ereignisses {@link RunElement#isClientCountStation()} geprft und dann ggf. {@link RunData#logClientLeavesStation(SimulationData, RunElement, RunElementData, RunDataClient)} aufgerufen.
  	 */
  	public static void addLeaveEvent(final SimulationData simData, final RunDataClient client, final RunElement station, final long timeDelta, final boolean statistics) {
  		/* Station-Event-Objekt holen */
  		final StationLeaveEvent leaveStation=(StationLeaveEvent)simData.getEvent(StationLeaveEvent.class);
  
  		/* ... und initialisieren */
  		leaveStation.init(simData.currentTime+timeDelta);
  
  		/* Konfiguration in Element eintragen */
  		leaveStation.station=station;
  		leaveStation.client=client;
  		leaveStation.statistics=statistics;
  
  		/* Zur Ereignisliste hinzufgen */
  		simData.eventManager.addEvent(leaveStation);
  	}
  
  	/**
  	 * Kndigt die Ankunft eines Kunden an der Zielstation an (damit {@link RunElementData#reportedClientsAtStation(SimulationData)} bereits den neuen Wert liefern kann)
  	 * @param simData	Simulationdatenobjekt
  	 * @param client	Kunde, der verschickt wird
  	 * @param nextStation	Station, an die der Kunde geschickt wird
  	 * @see RunElementData#announceClient(SimulationData, RunDataClient)
  	 * @see RunElementData#reportedClientsAtStation(SimulationData)
  	 */
  	public static void announceClient(final SimulationData simData, final RunDataClient client, final RunElement nextStation) {
  		final RunElementData data=nextStation.getData(simData);
  		data.announceClient(simData,client);
  		client.isAnnouncedToStation=true;
  	}
  
  	/**
  	 * Trgt den Kunden wieder aus der Liste der angekndigten Anknfte aus (weil er jetzt wirklich geschickt wird)
  	 * @param simData	Simulationdatenobjekt
  	 * @param client	Kunde, der verschickt wird
  	 * @param nextStation	Station, an die der Kunde geschickt wird
  	 * @see StationLeaveEvent#announceClient(SimulationData, RunDataClient, RunElement)
  	 * @see RunElementData#unannounceClient(SimulationData, RunDataClient)
  	 * @see RunElementData#reportedClientsAtStation(SimulationData)
  	 */
  	public static void unannounceClient(final SimulationData simData, final RunDataClient client, final RunElement nextStation) {
  		if (client.isAnnouncedToStation) {
  			final RunElementData data=nextStation.getData(simData);
  			data.unannounceClient(simData,client);
  			client.isAnnouncedToStation=false;
  		}
  	}
  }
  