// $Source: /usr/local/SimSoft/Sfx3D/src/gsl/vwagen.cpp,v $
// $Revision: 1.3 $
// $Author: baum $
// $Date: 2001/12/04 16:27:05 $
//
// Login name of the user who locked the revision
// $Locker:  $
// Tag name used to checkout the file
// $Name: Release-14 $
// State assigned to revision
// $State: Exp $
//
//
// $Log: vwagen.cpp,v $
// Revision 1.3  2001/12/04 16:27:05  baum
// -Verschiedene Bugs entfernt
//
// Revision 1.2  2001/11/29 15:18:10  baum
// -Realzeitsteuerung eingebaut
// -Handsteuerung eingebaut
//

#include <float.h>
#include <limits.h>
#include <iostream.h>

#include "MathDefs.h"
#include "Token.h"

#include "Vec2D.h"
#include "Vec3D.h"

#define _VWAGEN_CPP
#include "modell.h"
#include "vwagen.h"

#include "sfx.h"
#include "transp.h"
#include "baustein.h"
#include "ereignis.h"
#include "arbplan.h"
#include "regal.h"
#include "drehpuff.h"
#include "route.h"
#include "realzeit.h"

//---------------------------------------------------------
// Klasse typVerteilwagen
//---------------------------------------------------------

typVerteilwagen::typVerteilwagen (unsigned n, unsigned b)
 : typBaustein            (n, b),

   //***** gewaehlte Vorgabewerte:
   ObjFahrGeschwindigkeit (60.0),  // in m/min !!
   ObjFahrBeschleunigung  (0.0),   // in m/min2 !! (war 7200)
   ObjFahrVerzoegerung    (0.0),   // in m/min2 !! (war 3600)
   ObjFahrweg             (0.0),
   ObjHubGeschwindigkeit  (0.0),   // in m/min !!  (war 30)
   ObjHubBeschleunigung   (0.0),   // in m/min2 !! (war 5040)
   ObjHubVerzoegerung     (0.0),   // in m/min2 !! (war 2520)
   ObjHubweg              (0.0),
   //*****Ende Vorgabewerte
   
   ObjHubGeo              (NULL),

   ObjFahrRichtung        (EX),
   ObjZielKoord           (),
   ObjGabelKoord          (),

   ObjFindeMeldungFunktion (typVerteilwagen::findeFifoMeldung)
 { }

typVerteilwagen::~typVerteilwagen () {
  if (ObjHubGeo) delete ObjHubGeo;
}

/*virtual*/ void typVerteilwagen::typTGSlesen (InputFile& file) {
  typBaustein::typTGSlesen (file);
  ObjFahrRichtung = ObjGeometry.DirToSpace (ObjFahrRichtung).unit ();
  TGBPermPattern* myTGBPattern = MySFXModel->TGBLibrary ()->findPerm (ObjBasistyp, ObjVariante);
  const DynArray<GeoObject>& go (myTGBPattern->AllGeometries ());
  go.First ();
  if (go.Next ()) {
    ObjHubGeo = new GeoObject ();
    GeoObject* geo = new GeoObject ();
    geo->copyContents (*(go ()));
    geo->setParent (*ObjHubGeo);
    geo->setSelectionToMum ();
    ObjHubGeo->setParent (ObjGeometry);
    ObjHubGeo->setSelectionToMum ();
    //ObjHubGeo->copyContents (*(go ()));
    //ObjHubGeo->setParent (ObjGeometry);
    //ObjHubGeo->setSelectionToMum ();
    Vec3D v (ObjPalPlaetze[0]->Position ());
    ObjHubGeo->setPos (ObjGeometry.PtToLocal (v), 0);
  }
  if (ObjPalPlaetze.Count () != 1)
    sim_error ("VWAGEN", "typVerteilwagen::typTGSlesen",
               "Verteilwagenvariante mit != 1 Palettenplatz", ObjVariante, FATAL);
}

/*virtual*/ void typVerteilwagen::typTTPlesen (InputFile& file) {
  SFXToken tok;

  tok.nextToken (file);
  ObjAufnahmezeit = SimTime (atof (tok ()) * SimTime::SecondsPerMinute ());

  tok.nextToken (file);
  ObjFahrGeschwindigkeit = atof (tok ());

  file.jump (200); //EOL

  ObjHubGeschwindigkeit = ObjFahrGeschwindigkeit / 2;   //**Vorgabewert!
  ObjPhysKapazitaet = ObjKapazitaet = 1;
  ObjGabelKoord = ObjPalPlaetze [0]->Position ();
}

/*virtual*/ ostream& typVerteilwagen::typTTPschreiben (ostream& ostrm) const {
  ostrm.precision (2);
  ostrm << "TTP> ";
  Id_Schreiben (ostrm);
  ostrm << " ";
  ostrm.width (6);
  ostrm << ObjAufnahmezeit.inMinutes () << " ";
  ostrm.width (6);
  ostrm << ObjFahrGeschwindigkeit << " ";
  ostrm.width (6);
  ostrm << ObjHubGeschwindigkeit << endl;
  return ostrm;
}

/*virtual*/ void typVerteilwagen::typTBVlesen (InputFile& file) {
  typBaustein::typTBVlesen (file);

  if ((ObjZielBaustein->Basistyp () == tVerteilwagen)
      || (ObjZielBaustein->Basistyp () == tLaufkran) || (ObjZielBaustein->Basistyp () == tMehrfachLader))
    sim_error ("VWAGEN", "typVerteilwagen::typTBVlesen",
               "Veknuepfung mit Verteilwagen, MehrfachLader oder Laufkran nicht erlaubt", ObjNummer, FATAL);
}

/*virtual*/ void typVerteilwagen::typTSTlesen (InputFile& file) {
  typBaustein::typTSTlesen (file);
  setStrategie (ObjStrategie);
}

/*virtual*/ unsigned typVerteilwagen::setStrategie (unsigned stratNr) {
  ObjStrategie = stratNr;
  switch (ObjStrategie) {
    case 0:  ObjFindeMeldungFunktion = typVerteilwagen::findeFifoMeldung; 
             break;
    case 1:  ObjFindeMeldungFunktion = typVerteilwagen::findeKurzwegMeldung; 
             break;
    case 2:  ObjFindeMeldungFunktion = typVerteilwagen::findeWartezeitMeldung;
             break;
    case 3:  ObjFindeMeldungFunktion = typVerteilwagen::findeMinAvisIstMeldung;
             break;
    case 4:  ObjFindeMeldungFunktion = typVerteilwagen::findeMitnahmeMeldung;
             break;
//  case 5: ObjFindeMeldungFunktion = typVerteilwagen::findeTramMeldung; break;
    default: sim_error ("VWAGEN", "typVerteilwagen::setStrategie",
                                  "Strategie ist nicht vorhanden, setzte FIFO",
                                   ObjNummer, WARNING);
             ObjFindeMeldungFunktion = typVerteilwagen::findeFifoMeldung; 
             break;
  }
  return ObjStrategie;
}

/*virtual*/ void typVerteilwagen::a_aufnehmen () {
  typMeldung *zM = holeMeldung ();
  ObjZielBaustein->gibTransportEinheit (zM);
  aufnehmenEinleiten (ObjGabelKoord, ObjAufnahmezeit);
}

/*virtual*/  void typVerteilwagen::e_aufnehmen () {
  typPlatz *p (ObjTransportEinheit->AktPlatz ());
  if (p->Buchung () != ObjTransportEinheit)
    sim_error ("VWAGEN", "typVerteilwagen::e_aufnehmen",
               "TransportEinheit ist nicht auf Platz gebucht", ObjNummer, FATAL);
  p->setBuchung (NULL);
  ObjBuchung--;
  ObjTransportEinheit->positionieren (ObjGabelKoord);
//  zTransportEinheit->faerbeGestalt (default);

  ObjZielBaustein = ObjTransportEinheit->nextBaustein ();
  if (!ObjTransportEinheit->ZielPlatz ())
    ObjTransportEinheit->setZielPlatz (ObjZielBaustein->waehlePlatz (ObjTransportEinheit));
  ObjZielKoord = UebergabePos (*ObjZielBaustein);
  if ((p = ObjTransportEinheit->ZielPlatz ()) != NULL)
    ObjZielKoord += Vec3D (0, 0, p->Position ().Z () - ObjZielKoord.Z ());

  if (ObjStoerDauer > SimTime (0.0))
    planesofort (ETastoerung, ObjNummer);
  else {
    setZustand (AKTIVIERT);
    if (WegDifferenz ())  
      planesofort (ETafahren, ObjNummer, ObjZielBaustein->Nummer ());
    else planesofort (ETameldewarten, ObjNummer, 0);
  }
  planesofort (ETeabgeben, ObjTransportEinheit->Meldung ()->VonBaustein ()->Nummer (), ObjNummer);
}

/*virtual*/ void typVerteilwagen::fahr_takt () {
  if (ObjZustand != FAHREN) return;
  if (ObjTaktSchritte > 0) {
    ObjTaktSchritte--;

    double d;
    Vec3D v;
    if (ObjFahrweg) {
      d = ObjTaktZeit.inMinutes () * ObjFahrGeschwindigkeit;
      if (fabs (ObjFahrweg) > d)  ObjFahrweg -= (d *= Sign (ObjFahrweg));
      else  {
        d = ObjFahrweg;
        ObjFahrweg = 0.0;
      }

      v = Vec3D (ObjFahrRichtung * d);
      bewegen (v);
      ObjGabelKoord += v;
    }

    if (ObjHubweg) {
      d = ObjTaktZeit.inMinutes () * ObjHubGeschwindigkeit;
      if (fabs (ObjHubweg) > d)  ObjHubweg -= (d *= Sign (ObjHubweg));
      else  {
        d = ObjHubweg;
        ObjHubweg = 0.0;
      }
      v = Vec3D (0.0, 0.0, d);
      ObjGabelKoord += v;
      if (ObjHubGeo) ObjHubGeo->move (v, 0);
    }
      
    if (ObjPalPlaetze [0]->TransportEinheit ())
      ObjTransportEinheit->positionieren (ObjGabelKoord);
    if (ObjTaktSchritte)
      planedelta (ObjTaktZeit, ETfahrTakt, ObjNummer, 
                  ObjZielBaustein->Nummer (), ObjTaktSchritte);
  }
}

/*virtual*/ void typVerteilwagen::a_fahren () {
  double fahrZeit = Zeit_ermitteln (fabs (ObjFahrweg), ObjFahrGeschwindigkeit,
                                    ObjFahrBeschleunigung, ObjFahrVerzoegerung);
  double hubZeit = Zeit_ermitteln (fabs (ObjHubweg), ObjHubGeschwindigkeit,
                                   ObjHubBeschleunigung, ObjHubVerzoegerung);
  double zeit = (hubZeit > fahrZeit) ? hubZeit : fahrZeit;
  setZustand (FAHREN);
//  faerbeGestalt (SimFenster->HigFarbe ());
  if (MySFXModel->RealTime ())  RzsSenden (ObjNummer, ETafahren, (int) ObjFahrweg);
  planedelta (zeit, ETefahren, ObjNummer, ObjZielBaustein->Nummer ());
  if (MySFXModel->Cycle ()) {
    ObjTaktZeit = (ClassFahrSchritt * SimTime::SecondsPerMinute ())
                  / ((ObjFahrGeschwindigkeit > ObjHubGeschwindigkeit) ?
                                     ObjFahrGeschwindigkeit : ObjHubGeschwindigkeit);
    ObjTaktSchritte = (unsigned) ((zeit - (ObjTaktZeit.inMinutes () / ClassTaktFaktor)) / ObjTaktZeit.inMinutes ());
    if (MySFXModel->Debug ()) {
         dprintf ("Fahren %3d: Fahrweg=%5.2f Hubweg=%5.2f Schritte=%1d Ziel = %1d zum %s\n",
                  ObjNummer, ObjFahrweg, ObjHubweg, ObjTaktSchritte,
                  ObjZielBaustein->Nummer (), ObjPalPlaetze [0]->TransportEinheit () ? "bringen" : "holen");
         dprintf ("            fahrZeit=%8.5f HubZeit=%8.5f zeit=%8.5f TaktZeit=%8.5f\n",
                  fahrZeit, hubZeit, zeit, ObjTaktZeit.inMinutes ());
         dprintf ("            Gabel= (%6.3f/%6.3f/%6.3f), Ziel= (%6.3f/%6.3f/%6.3f)\n",
                  ObjGabelKoord.X (), ObjGabelKoord.Y (), ObjGabelKoord.Z (),
                  ObjZielKoord.X (), ObjZielKoord.Y (), ObjZielKoord.Z ());
    }
    if (ObjTaktSchritte > 0)  fahr_takt ();
  }
  if (((!MySFXModel->Cycle ()) || (ObjTaktSchritte == 0)) && (ObjFahrweg != 0.0)) {
    Vec3D v (ObjZielKoord - ObjGabelKoord);
    positionieren (ObjGeometry.Pos () + v.projOn (ObjFahrRichtung));
    ObjGabelKoord = ObjZielKoord;
    if (ObjHubGeo) ObjHubGeo->setPos (ObjGeometry.PtToLocal ((v = ObjGabelKoord)), 0);
    if (ObjPalPlaetze [0]->TransportEinheit ())  ObjTransportEinheit->positionieren (ObjZielKoord);
  }
}

/*virtual*/ void typVerteilwagen::e_fahren() {

	if ( WegDifferenz() ) {
//  if (ObjZielKoord != ObjGabelKoord) {
    Vec3D v (ObjZielKoord - ObjGabelKoord);
    positionieren (ObjGeometry.Pos () + v.projOn (ObjFahrRichtung));
    ObjGabelKoord = ObjZielKoord;
    if (ObjHubGeo) ObjHubGeo->setPos (ObjGeometry.PtToLocal ((v = ObjGabelKoord)), 0);
    if (ObjPalPlaetze [0]->TransportEinheit ())  ObjTransportEinheit->positionieren (ObjZielKoord);
  }

  if (ObjStoerDauer > SimTime (0.0))
    planesofort (ETastoerung, ObjNummer);
  else {
    setZustand (AKTIVIERT);
    if (ObjPalPlaetze [0]->TransportEinheit () && 
        ObjTransportEinheit->ZielPlatz ())  {
      ObjZielBaustein = NULL;
      planesofort (ETameldewarten, ObjNummer, 0);
    }
    else
    //lars
    	 if (ObjSperrung) planesofort(ETasperrung,ObjNummer,ObjNummer);
    	 else planesofort (ETaktivieren, ObjNummer, ObjNummer);
  }
}

/*virtual*/ void typVerteilwagen::eStoeren () {
  ObjStoerEnde = NULL;
  if (ObjStoerGeschichte) ObjStoerGeschichte->erfassen (0.0);
  switch (ObjAlterZustand) {
    case AUFNEHMEN :  setZustand (AKTIVIERT);
                      if (WegDifferenz ())  
                        planesofort (ETafahren, ObjNummer, ObjZielBaustein->Nummer ());
                      else planesofort (ETameldewarten, ObjNummer, 0);
                      break;

    case FAHREN    :  setZustand (AKTIVIERT);
                      if (ObjPalPlaetze [0]->TransportEinheit () && 
                          ObjTransportEinheit->ZielPlatz ())  {
                        ObjZielBaustein = NULL;
                        planesofort (ETameldewarten, ObjNummer, 0);
                      }
                      else
                        planesofort (ETaktivieren, ObjNummer, ObjNummer);
                      break;

    default        :  setZustand (ObjAlterZustand); // RUHE or POLLING!!!
                      planesofort (ETaktivieren, ObjNummer, ObjNummer);
                      aktiviereEingaenge ();
                      aktiviereAusgaenge ();
                      break;
  }
}

/*virtual*/ bool typVerteilwagen::ausrichtenAufBaustein (typBaustein* zielBst) {
  if ((ObjZustand == RUHE) || (ObjZustand == POLLING)) {
    ObjZielBaustein = zielBst;
    ObjZielKoord = UebernahmePos (*ObjZielBaustein);
    if (WegDifferenz ()) {
      setZustand (AKTIVIERT);
      planesofort (ETafahren, ObjNummer, ObjZielBaustein->Nummer ());
    }
    return true;
  }
  return false;
}

/*virtual*/ bool typVerteilwagen::hasKapazitaet (typTransportEinheit*) const {
  return (ObjBelegung + ObjBuchung) < ObjKapazitaet;
}

/*virtual*/ typPlatz* typVerteilwagen::waehlePlatz (typTransportEinheit* zTE) {
  typPlatz* p (ObjPalPlaetze [0]);
  if (!p->isVerfuegbar ())  return (p->Buchung () == zTE) ? p : (typPlatz*) NULL;
  p->setBuchung (zTE);
  ObjBuchung++;
  return p;
}

/*virtual*/ Vec3D typVerteilwagen::UebernahmePos (const typBaustein& von) const {
  Vec3D v (von.UebergabePos (*this));
  double zielZ (v.Z ());
  v = (v - ObjGabelKoord).projOn (ObjFahrRichtung);
  v += ObjGabelKoord;
  return Vec3D (v.X (), v.Y (), zielZ);
}

/*virtual*/ Vec3D typVerteilwagen::UebergabePos (const typBaustein& nach) const {
  Vec3D v (nach.UebernahmePos (*this));
  double zielZ (v.Z ());
  v = (v - ObjGabelKoord).projOn (ObjFahrRichtung);
  v += ObjGabelKoord;
  return Vec3D (v.X (), v.Y (), zielZ);
}

/*virtual*/ const double& typVerteilwagen::setGeschwindigkeit (const double& wert) {
  if (wert <= 0.0)
    sim_error ("VWAGEN", "typVerteilwagen::setGeschwindigkeit",
               "negative Fahrgeschwindigkeit", ObjNummer, FATAL);
  return ObjFahrGeschwindigkeit = wert;
}

const double& typVerteilwagen::setFahrBeschleunigung (const double& wert) {
  if (wert <= 0.0)
    sim_error ("VWAGEN", "typVerteilwagen::setFahrBeschleunigung",
               "negative Fahrbeschleunigung", ObjNummer, FATAL);
  return ObjFahrBeschleunigung = wert;
}

const double& typVerteilwagen::setFahrVerzoegerung (const double& wert) {
  if (wert <= 0.0)
    sim_error ("VWAGEN", "typVerteilwagen::setFahrVerzoegerung",
               "negative Fahrverzoegerung", ObjNummer, FATAL);
  return (ObjFahrVerzoegerung = wert);
}

const double& typVerteilwagen::setHubGeschwindigkeit (const double& wert) {
  if (wert <= 0.0)
    sim_error ("VWAGEN", "typVerteilwagen::setHubGeschwindigkeit",
               "negative Hubgeschwindigkeit", ObjNummer, FATAL);
  return ObjHubGeschwindigkeit = wert;
}

const double& typVerteilwagen::setHubBeschleunigung (const double& wert) {
  if (wert <= 0.0)
    sim_error ("VWAGEN", "typVerteilwagen::setHubBeschleunigung",
               "negative Hubbeschleunigung", ObjNummer, FATAL);
  return ObjHubBeschleunigung = wert;
}

const double& typVerteilwagen::setHubVerzoegerung (const double& wert) {
  if (wert <= 0.0)
    sim_error ("VWAGEN", "typVerteilwagen::setHubVerzoegerung",
               "negative Hubverzoegerung", ObjNummer, FATAL);
  return (ObjHubVerzoegerung = wert);
}

/*virtual*/ void typVerteilwagen::individuellAktivieren (typBaustein* vonWem) {
  if ((ObjZustand == RUHE) || (ObjZustand == POLLING) || (vonWem == this)
      || ((ObjZustand == WARTEDP) && (ObjZielBaustein == vonWem))) {
    if (ObjPalPlaetze [0]->TransportEinheit ())  {
      if (!ObjTransportEinheit->ZielPlatz ())
        if (ObjTransportEinheit->setZielPlatz (ObjZielBaustein->waehlePlatz (ObjTransportEinheit))) {
          ObjZielKoord += Vec3D (0, 0, ObjTransportEinheit->ZielPlatz ()->Position ().Z () - ObjZielKoord.Z ());
          if (WegDifferenz ())
            planesofort (ETafahren, ObjNummer, ObjZielBaustein->Nummer ());
          else  planesofort (ETameldewarten, ObjNummer, 0);
        }
        else  planesofort (ETameldewarten, ObjNummer, 0);
      return;
    }
    else
      setZustand (RUHE);  // 29.03.00 rei

    typMeldung* zM = findeAusfuehrbareMeldung ();
    if (zM) {
      ObjTransportEinheit = zM->TransportEinheit ();
      ObjZielBaustein = zM->VonBaustein ();
      Vec3D v (UebernahmePos (*ObjZielBaustein));
      ObjZielKoord = Vec3D (v.X (), v.Y (),
                     ObjTransportEinheit->AktPlatz ()->Position ().Z ());
      if (WegDifferenz ()) {
        setZustand (AKTIVIERT);
        planesofort (ETafahren, ObjNummer, ObjZielBaustein->Nummer ());
      }
      else  wartenaufnehmen (ObjTransportEinheit, ObjZielBaustein);
    }
    else {
      if (ObjEingaenge.Count () == 1) {
        ObjZielBaustein = ObjEingaenge.First ();
        ObjZielKoord = UebernahmePos (*ObjZielBaustein);
        if (WegDifferenz ()) {
          setZustand (AKTIVIERT);
          planesofort (ETafahren, ObjNummer, ObjZielBaustein->Nummer ());
        }
        else {
           if (!(ObjMeldungen.isEmpty ())) {
             setZustand (POLLING);
             // printMeldeListe (); // ist fuer deadlock-Situationen nuetzlich
           }
           else {
            setZustand (RUHE);
           }
        }
      }
      else {
        if (!(ObjMeldungen.isEmpty ())) {
          setZustand (POLLING);
          // printMeldeListe ();  // ist fuer deadlock-Situationen nuetzlich
        }
	     else setZustand (RUHE);
	   }
	}
  }
}

typMeldung* typVerteilwagen::findeAusfuehrbareMeldung () {
  return ObjFindeMeldungFunktion (this);
}

/*static*/  typMeldung* typVerteilwagen::findeFifoMeldung (typVerteilwagen* who) {
  typTransportEinheit* zBuch = who->ObjPalPlaetze [0]->Buchung ();
  for (LCursor<typMeldung> c (who->ObjMeldungen); c; c++) {
    typTransportEinheit* zTE = c ()->TransportEinheit ();
    if (!zBuch || (zTE == zBuch)) {
      if (!zTE->BuchPlatz ()) {
        typBaustein* zNext = zTE->nextBaustein ();
        if (zNext->hasKapazitaet (zTE))  zTE->setBuchPlatz (zNext->waehlePlatz (zTE));
      }
      if (((who->ObjEingaenge.Count () > 1) || (who->ObjAusgaenge.Count () > 1)) && (!zTE->BuchPlatz ())) continue;
      if (!zBuch)  zTE->setZielPlatz (who->waehlePlatz (zTE));
      return &(who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (c))));
    }
  }
  return NULL;
}

/*static*/ typMeldung* typVerteilwagen::findeKurzwegMeldung (typVerteilwagen* who) {
  typTransportEinheit* zBuch = who->ObjPalPlaetze [0]->Buchung ();
  typMeldung* zM;

  for (unsigned i = who->ObjMeldungen.Count (); i; i--) {
    Vec3D min (DBL_MAX, DBL_MAX, DBL_MAX);
    LCursor<typMeldung> c (who->ObjMeldungen, 1); // toEnd
    for (unsigned j = 1; j <= i; c--, j++) {
      Vec3D v (c ()->TransportEinheit ()->AktPlatz ()->Position () - who->ObjGabelKoord);
      if (v.len () < min.len ()) {
        min = v;
        zM = c ();
      }
    }
    typTransportEinheit* zTE = zM->TransportEinheit ();
    if (!zBuch || (zTE == zBuch)) {
      if (!zTE->BuchPlatz ()) {
        typBaustein* zNext = zTE->nextBaustein ();
        if (zNext->hasKapazitaet (zTE))  zTE->setBuchPlatz (zNext->waehlePlatz (zTE));
      }
      if (((who->ObjEingaenge.Count () > 1) || (who->ObjAusgaenge.Count () > 1)) && (!zTE->BuchPlatz ())) continue;
      if (!zBuch)  zTE->setZielPlatz (who->waehlePlatz (zTE));
      return &(who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (*zM))));
    }
    who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (*zM)));
  }
  return NULL;
}

/*static*/ typMeldung* typVerteilwagen::findeWartezeitMeldung (typVerteilwagen* who) {
  typTransportEinheit* zBuch = who->ObjPalPlaetze [0]->Buchung ();
  typMeldung* zM = NULL;

  for (unsigned i = who->ObjMeldungen.Count (); i; i--) {
    double maxWarteZeit = -1.0;
    LCursor<typMeldung> c (who->ObjMeldungen, 1); // toEnd
    for (unsigned j = 1; j <= i; c--, j++) {
      typBaustein* bst = c ()->VonBaustein ();
      double aktWarteZeit = bst->ZustandZeit ();
      if (bst->Zustand () == WARTEN &&
          aktWarteZeit > who->ObjWartezeitSchwelle &&
          aktWarteZeit > maxWarteZeit) {
        maxWarteZeit = aktWarteZeit;
        zM = c ();
      }
    }
    if (zM) {
      typTransportEinheit* zTE = zM->TransportEinheit ();
      if (!zBuch || (zTE == zBuch)) {
        if (!zTE->BuchPlatz ()) {
          typBaustein* zNext = zTE->nextBaustein ();
          if (zNext->hasKapazitaet (zTE))  zTE->setBuchPlatz (zNext->waehlePlatz (zTE));
        }
        if (((who->ObjEingaenge.Count () > 1) || (who->ObjAusgaenge.Count () > 1)) && (!zTE->BuchPlatz ())) continue;
        if (!zBuch)  zTE->setZielPlatz (who->waehlePlatz (zTE));
        return &(who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (*zM))));
      }
      who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (*zM)));
    }
    else break;
  }
  return typVerteilwagen::findeFifoMeldung (who);
}

/*static*/ typMeldung* typVerteilwagen::findeMinAvisIstMeldung (typVerteilwagen* who) {
  typTransportEinheit* zBuch = who->ObjPalPlaetze [0]->Buchung ();
  typMeldung* zM = NULL;

  for (unsigned i = who->ObjMeldungen.Count (); i; i--) {
    int minAvis = INT_MAX;
    LCursor<typMeldung> c (who->ObjMeldungen);
    for (unsigned j = 1; j <= i; c++, j++) {
      int aktAvis = c ()->TransportEinheit ()->Vorgang ()->ZielBaustein ()->Avis ()->DirIstAnzahl ();
      if (aktAvis < minAvis) {
        minAvis = aktAvis;
        zM = c ();
      }
    }
    typTransportEinheit* zTE = zM->TransportEinheit ();
    if (!zBuch || (zTE == zBuch)) {
      if (!zTE->BuchPlatz ()) {
        typBaustein* zNext = zTE->nextBaustein ();
        if (zNext->hasKapazitaet (zTE))  zTE->setBuchPlatz (zNext->waehlePlatz (zTE));
      }
      if (((who->ObjEingaenge.Count () > 1) || (who->ObjAusgaenge.Count () > 1)) && (!zTE->BuchPlatz ())) continue;
      if (!zBuch)  zTE->setZielPlatz (who->waehlePlatz (zTE));
      return &(who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (*zM))));
    }
    who->ObjMeldungen.append (*(who->ObjMeldungen.remove (*zM)));
  }
  return NULL;
}

/*static*/ typMeldung* typVerteilwagen::findeMitnahmeMeldung (typVerteilwagen* who) {
  typTransportEinheit* zBuch = who->ObjPalPlaetze [0]->Buchung ();
  typMeldung* fifoMldg = NULL;
  for (LCursor<typMeldung> c (who->ObjMeldungen); c; c++) {
    typTransportEinheit* zTE = c ()->TransportEinheit ();
    if (!zBuch || (zTE == zBuch)) {
      if (((who->ObjEingaenge.Count () > 1) || (who->ObjAusgaenge.Count () > 1))
          && (!zTE->BuchPlatz ()) && (!zTE->nextBaustein ()->hasKapazitaet (zTE))) continue;
      fifoMldg = &(who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (c))));
      break;
    }
  }

  if (fifoMldg) {
    double minAbstand = DBL_MAX;
    Vec3D fifoDir (fifoMldg->TransportEinheit ()->AktPlatz ()->Position () - who->ObjGabelKoord);
    fifoDir = fifoDir.projOn (who->ObjFahrRichtung);
    LCursor<typMeldung> mitnahme;
    LCursor<typMeldung> c (who->ObjMeldungen);
    for (c++; c; c++) {
      typTransportEinheit* zTE = c ()->TransportEinheit ();
      typBaustein* zNext = zTE->nextBaustein ();
      if ((zBuch || (zTE != zBuch))    // kann dieses TransportEinheit ueberhaupt befoerdert werden????
          && ((who->ObjEingaenge.Count () > 1) || (who->ObjAusgaenge.Count () > 1))
          && (!zTE->BuchPlatz ()) && (!zTE->nextBaustein ()->hasKapazitaet (zTE)))
        continue;
      else {
        Vec3D targetDir (zNext->UebernahmePos (*who) - who->ObjGabelKoord);
        targetDir = targetDir.projOn (who->ObjFahrRichtung);
        if ((targetDir.hasSameDir (fifoDir)) && (targetDir.len () <= fifoDir.len ())) {
          Vec3D fetchDir (zTE->AktPlatz ()->Position () - who->ObjGabelKoord);
          fetchDir = fetchDir.projOn (who->ObjFahrRichtung);
          double aktAbstand = fetchDir.len ();
          if (fetchDir.hasSameDir (fifoDir)
              && (aktAbstand < fifoDir.len ())
              && (aktAbstand < minAbstand)) {
                mitnahme = c;
                minAbstand = aktAbstand;
          }
        }
      }
    }
    if (mitnahme ()) {
      typTransportEinheit* mitnahmeTreffer = mitnahme ()->TransportEinheit ();
      typBaustein* mitnahmeZiel = mitnahmeTreffer->nextBaustein ();
      if (!mitnahmeTreffer->BuchPlatz ())
        mitnahmeTreffer->setBuchPlatz (mitnahmeZiel->waehlePlatz (mitnahmeTreffer));
      if (!zBuch)  mitnahmeTreffer->setZielPlatz (who->waehlePlatz (mitnahmeTreffer));
      return &(who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (mitnahme))));
    }
  }
  return typVerteilwagen::findeFifoMeldung (who);
}

/*static*/ typMeldung* typVerteilwagen::findeTramMeldung (typVerteilwagen* /*who*/) {
  return NULL;
}

bool typVerteilwagen::WegDifferenz () {
  Vec3D v (ObjZielKoord - ObjGabelKoord);
  ObjHubweg = v.Z ();
  v = v.projOn (ObjFahrRichtung);
  ObjZielKoord = ObjGabelKoord + Vec3D (v.X (), v.Y (), ObjHubweg);
  ObjFahrweg = v.len ();
  if (!v.hasSameDir (ObjFahrRichtung))  ObjFahrweg *= -1;
  return (ObjFahrweg || ObjHubweg) ? true : false;
}

double typVerteilwagen::Zeit_ermitteln (const double& weg, const double& geschw,
                                  const double& beschl, const double& verz) const {
  if (beschl && verz) {
    double f = (1 / beschl) + (1 / verz);  // Rechenfaktor f. Beschleunigungen
    double v = sqrt (2 * weg / f);         // erreichbare Geschwindigkeit
    if (v > geschw)                        // Trapezrechnung
      return (geschw * f) + ((weg - (geschw * geschw / 2 * f)) / geschw);
    else
      return v * f;                     // Dreieckrechnung
  }
  else
    return weg / geschw;                // Rechteckrechnung
}

void typVerteilwagen::printMeldeListe () {
  typTransportEinheit* zBuch = ObjPalPlaetze [0]->Buchung ();
  if (zBuch) dprintf ("Buchung vorhanden von %d nach %d\n", 
                      zBuch->Baustein ()->Nummer (), 
                      zBuch->nextBaustein ()->Nummer ());
  else dprintf ("keine Buchung vorhanden\n");
  dprintf ("Meldungen :\n");
  for (LCursor<typMeldung> c (ObjMeldungen); c; c++) {
    typTransportEinheit* zTE = c ()->TransportEinheit ();
    dprintf ("von %d nach %d\n", zTE->Baustein ()->Nummer (), 
                                 zTE->nextBaustein ()->Nummer ());
 }
}

