// $Source: /usr/local/SimSoft/Sfx3D/src/gsl/regalver.cpp,v $
// $Revision: 1.2 $
// $Author: baum $
// $Date: 2001/11/29 15:18:10 $
//
// Login name of the user who locked the revision
// $Locker:  $
// Tag name used to checkout the file
// $Name:  $
// State assigned to revision
// $State: Exp $
//
//
// $Log: regalver.cpp,v $
// Revision 1.2  2001/11/29 15:18:10  baum
// -Realzeitsteuerung eingebaut
// -Handsteuerung eingebaut
//

#include <limits.h>

#include "Token.h"
#include "List.h"

#define _REGALVER_CPP
#include "modell.h"
#include "regalver.h"

#include "sfx.h"
#include "transp.h"
#include "baustein.h"
#include "arbplan.h"
#include "regal.h"
#include "programm.h"

//---------------------------------------------------------
// Klasse typRegalverwalter
//---------------------------------------------------------

typRegalverwalter::typRegalverwalter (unsigned n, unsigned b)
  : typBaustein      (n, b),
    ObjBetreuer      (NULL),
    ObjVerwalter     (NULL),
    ObjBestellFaehig (),
    ObjBetreuTeile   (),
    ObjArbeitsliste  (),
    ObjZuweisungen   (),
    ObjRegale        (1) {
  ObjAvis = new typAvis ();
}

/*virtual*/ typRegalverwalter::~typRegalverwalter ()  {
  while (ObjArbeitsliste.Last ())
    delete ObjArbeitsliste.removeLast ();
}

/*virtual*/ void typRegalverwalter::typTGSlesen (InputFile& file) {
  typBaustein::typTGSlesen (file);
  if (ObjPalPlaetze.Count ())
    sim_error ("REGALVER", "typRegalverwalter::typTGSlesen",
               "Regalverwalter mit Palettenplatz", ObjVariante, FATAL);
}

/*virtual*/ void typRegalverwalter::typTPVlesen (InputFile& file) {
  SFXToken tok;
  unsigned anzahl;
  unsigned nr, z;

  tok.nextToken (file);
  anzahl = atoi (tok ());

  for (z = 1; z <= anzahl; z++) {
    tok.nextToken (file);
    nr = atoi (tok ());
    ObjRegale.append (*((typRegaleinheit*) baustein [nr]));
    baustein [nr]->setVerwalter (this);
  }
  file.jump (200); //EOL

  tok.nextToken (file);
  anzahl = atoi (tok ());

  for (z = 1; z <= anzahl; z++) {
    tok.nextToken (file);
    nr = atoi (tok ());
    baustein [nr]->setBetreuer (this);
  }
  file.jump (200); //EOL
}

/*virtual*/ void typRegalverwalter::a_meldewarten () {
  sim_error ("REGALVER", "typRegalverwalter::a_meldewarten",
             "a_meldewarten unzulaessig fuer Regalverwalter", ObjNummer, FATAL);
}

/*virtual*/ unsigned typRegalverwalter::AnzahlArbeitsliste () const {
  return ObjArbeitsliste.Count ();
}

/*virtual*/ bool typRegalverwalter::hasKapazitaet (typTransportEinheit*) const {
  for (ObjRegale.First (); ObjRegale (); ObjRegale++)
    if (isZuweisbar (ObjRegale ()))  return true;
  return false;
}

/*virtual*/ unsigned typRegalverwalter::Kapazitaet () const {
  unsigned kap = 0;
  for (ObjRegale.First (); ObjRegale (); ObjRegale++)  kap += ObjRegale ()->Kapazitaet ();
  return kap;
}

/*virtual*/ unsigned typRegalverwalter::setKapazitaet (unsigned) {
  return sim_error ("REGALVER", "typRegalverwalter::setKapazitaet",
                    "setKapazitaet unzulaessig fuer Regalverwalter", ObjNummer, FATAL);
}

void typRegalverwalter::Anforderung_naechste_TE (typBaustein *zB) {
  for (LCursor<typTransportEinheit> c (ObjBetreuTeile); c; c++)
    if (c ()->Vorgang ()->ZielBaustein () == zB) {
      ObjBetreuTeile.remove (*c ())->Anmelden ();
      return;
    }
}

typBaustein* typRegalverwalter::PoolBaustein (unsigned bstNr) const {
  for (ObjRegale.First (); ObjRegale (); ObjRegale++)
    if (ObjRegale ()->Nummer () == bstNr)  return ObjRegale ();
  return NULL;
}

/*virtual*/ void typRegalverwalter::ArbZuweisen (unsigned was,
              unsigned wohin, unsigned istVorg, unsigned sollVorg,
              unsigned wieoft) {
  if (PoolBaustein (wohin)) {
    Zuweisung *zw = new ArbeitsplanZuweisung (was, wohin, istVorg, sollVorg, wieoft);
    ObjZuweisungen.append (*zw);
  }
  else
    sim_error ("BAUSTEIN", "typRegalverwalter::ArbZuweisen",
               "Regal nicht im Pool vorhanden", wohin, FATAL);
}

/*virtual*/ void typRegalverwalter::ArtZuweisen (unsigned was,
              unsigned wohin, unsigned istVorg, unsigned sollVorg,
              unsigned wieoft) {
  if (PoolBaustein (wohin)) {
    Zuweisung *zw = new ArtikelZuweisung (was, wohin, istVorg, sollVorg, wieoft);
    ObjZuweisungen.append (*zw);
  }
  else
    sim_error ("BAUSTEIN", "typRegalverwalter::ArtZuweisen",
               "Baustein nicht im Pool vorhanden", wohin, FATAL);
}

typBaustein* typRegalverwalter::ZuweisungAusfuehren
                                   (typTransportEinheit* zTE) {
  if (ObjZuweisungen.isEmpty ()) return NULL;
  for (LCursor<Zuweisung> z (ObjZuweisungen); z; z++)
    if (z ()->TransportEinheitPasst (zTE)) {
      if (z ()->SollVorgang ())
        zTE->schalteVorgang (z ()->SollVorgang ());
        if (! (z ()->decWieoft ())) {
        typBaustein *ziel = PoolBaustein (z ()->FuerBaustein ());
        delete ObjZuweisungen.remove (*z ());
        return ziel;
      }
    }
  return NULL;
}

/*virtual*/ typBaustein* typRegalverwalter::setZielBaustein (typTransportEinheit *zTE) {
  typRegaleinheit* ziel = (typRegaleinheit*) ZuweisungAusfuehren (zTE);
  if (ziel) return ziel;
  if (ObjBetreuer && !(ObjAvis->isDirAvisOk ())) {
    ziel = ObjBetreuer->Regal_zuweisen (zTE); // erweitert wegen Zielorientiert einlagern
  }
  else {
    ziel = Regal_zuweisen (zTE);
    if (zTE->Vorgang ()->ZielBaustein () == this)  ObjAvis->decIndIstAnzahl ();
    ObjAvis->incDirIstAnzahl ();
    ziel->Avis ()->incDirIstAnzahl ();
    ziel->incReservierungen();
    (zTE->Baustein ())->Avis ()->decDirIstAnzahl ();
  }
  return ziel;
}

bool typRegalverwalter::WerkAusliefern (typTransportEinheit *zTE) {
   for (LCursor<Bestellzettel> c (ObjArbeitsliste); c; c++) {
      if (c()->TransportEinheitPasst(zTE)) {
         if (c()->SollVorgang() > 0) {
            zTE->schalteVorgang(c()->SollVorgang());
         }
         if (!(c()->decWieoft())) {
            delete ObjArbeitsliste.remove(c);
         }
         return true;
      }
   }
   return ObjVerwalter ? ObjVerwalter->WerkAusliefern (zTE) : false;
}

/*virtual*/ void typRegalverwalter::individuellAktivieren (typBaustein*) {
// Diese Funktion ist absichtlich leer!!!!
}

/*virtual*/ typTransportEinheit* typRegalverwalter::GibProjektIDTE (unsigned idnr /*= 0*/, unsigned projektID /*= 0*/, unsigned suchID /*= 0*/) {
   for (LCursor<typTransportEinheit> c (ObjBestellFaehig); c; c++) {
      if ((c()->ProjektID() == suchID) && (idnr ? (c()->Artikel() == idnr) : true)) {
         if (projektID) {
            c()->setProjektID(projektID);
         }
         return c();
      }
   }
   return NULL;
}

void typRegalverwalter::DruckeArbeitsListe () const {
   cout << "DruckeArbeitsListe von Regalverwalter Nr. " << Nummer() << endl;
   int i = 1;
   for (LCursor<Bestellzettel> c (ObjArbeitsliste); c; c++, i++) {
      cout << "Bestellzettel " << i << ":" << endl;
      c()->DruckeBestellZettel();
   }
}

void typRegalverwalter::BestAusliefern (Bestellzettel *zBZ) {
  for (LCursor<typTransportEinheit> c (ObjBestellFaehig); c; c++)
    if (zBZ->TransportEinheitPasst (c ()))  {
      if (zBZ->SollVorgang ())  c ()->schalteVorgang (zBZ->SollVorgang ());
      ((typRegaleinheit*) c ()->Baustein ())->Ausliefern (c ());
      if (! (zBZ->decWieoft ())) {
        delete ObjArbeitsliste.remove (*zBZ);
        return;
      }
    }
}

/*virtual*/ void typRegalverwalter::ArbBestellung
                        (unsigned idnr, unsigned istvorg, unsigned sollvorg, unsigned wieoft) {
   Bestellzettel *zBZ = new ArbeitsplanBestellzettel (idnr, istvorg, sollvorg, wieoft);
   ObjArbeitsliste.append (*zBZ);
   BestAusliefern (zBZ);
}

/*virtual*/ void typRegalverwalter::TEBestellung
                        (typTransportEinheit *te, unsigned sollvorg) {
   if (!te) {
      return (void) sim_error ("BAUSTEIN", "typRegalverwalter::TEBestellung",
               "Zeiger auf TransportEinheit ist NULL", ObjNummer, FATAL);
   }
   else {
      for (LCursor<Bestellzettel> c (ObjArbeitsliste); c; c++) {
         if (c()->TransportEinheitPasst(te) && !c()->IdentNr()) {
            return (void) sim_error ("BAUSTEIN", "typRegalverwalter::TEBestellung",
               "TransportEinheit ist schon bestellt!", ObjNummer, FATAL);
         }
      }
      Bestellzettel *zBZ = new TEBestellzettel (te, sollvorg);
      ObjArbeitsliste.append (*zBZ);
      BestAusliefern (zBZ);
   }
}

/*virtual*/ void typRegalverwalter::ArtBestellung
                        (unsigned idnr, unsigned istvorg, unsigned sollvorg, unsigned wieoft) {
   Bestellzettel *zBZ = new ArtikelBestellzettel (idnr, istvorg, sollvorg, wieoft);
   ObjArbeitsliste.append (*zBZ);
   BestAusliefern (zBZ);
}

/*virtual*/ bool typRegalverwalter::loescheArbBestellung (unsigned idnr, unsigned istv) {
  for (LCursor<Bestellzettel> c (ObjArbeitsliste); c; c++) {
    if (c ()->hatArbeitsplan (idnr, istv)) {
      delete ObjArbeitsliste.remove (c);
      return true;
    }
  }
  return false;
}

/*virtual*/ bool typRegalverwalter::loescheTEBestellung (typTransportEinheit *te) {
   for (LCursor<Bestellzettel> c (ObjArbeitsliste); c; c++) {
      if (c()->TransportEinheitPasst(te)) {
         delete ObjArbeitsliste.remove (c);
         return true;
      }
   }
   return false;
}

/*virtual*/ bool typRegalverwalter::loescheArtBestellung (unsigned idnr, unsigned istv) {
  for (LCursor<Bestellzettel> c (ObjArbeitsliste); c; c++) {
    if (c ()->hatArtikel (idnr, istv)) {
      delete ObjArbeitsliste.remove (c);
      return true;
    }
  }
  return false;
}

/*virtual*/ unsigned typRegalverwalter::AnzahlArbeitsplanVorhanden (unsigned arbNr, unsigned istv /*= 0*/) const {
  unsigned anzTE = 0;
  for (ObjRegale.First (); ObjRegale (); ObjRegale++)
    anzTE += ObjRegale ()->AnzahlArbeitsplanVorhanden (arbNr, istv);
  return anzTE;
}

/*virtual*/ unsigned typRegalverwalter::AnzahlArtikelVorhanden (unsigned artNr, unsigned istv /*= 0*/) const {
  unsigned anzTE = 0;
  for (ObjRegale.First (); ObjRegale (); ObjRegale++)
    anzTE += ObjRegale ()->AnzahlArtikelVorhanden (artNr, istv);
  return anzTE;
}

/*virtual*/ double typRegalverwalter::MengeArbeitsplanVorhanden (unsigned arbNr, unsigned istv /*= 0*/) const {
  double mengeTE = 0;
  for (ObjRegale.First (); ObjRegale (); ObjRegale++)
    mengeTE += ObjRegale ()->MengeArbeitsplanVorhanden (arbNr, istv);
  return mengeTE;
}

/*virtual*/ double typRegalverwalter::MengeArtikelVorhanden (unsigned artNr, unsigned istv /*= 0*/) const {
  double mengeTE = 0;
  for (ObjRegale.First (); ObjRegale (); ObjRegale++)
    mengeTE += ObjRegale ()->MengeArtikelVorhanden (artNr, istv);
  return mengeTE;
}

/*virtual*/ unsigned typRegalverwalter::AnzahlArbeitsplanBestellbar (unsigned idnr, unsigned istv) const {
  unsigned anzTE = 0;
  for (LCursor<typTransportEinheit> c (ObjBestellFaehig); c; c++)
    if (c ()->hatArbeitsplan (idnr, istv))  anzTE++;
  return anzTE;
}

/*virtual*/ unsigned typRegalverwalter::AnzahlProjektIDBestellbar (unsigned artNr, unsigned suchID) const {
   unsigned anzTE = 0;
   for (LCursor<typTransportEinheit> c (ObjBestellFaehig); c; c++) {
      if ((c()->ProjektID() == suchID) && (artNr ? (c()->Artikel() == artNr) : true)) {
         anzTE++;
      }
   }
   return anzTE;
}

/*virtual*/ unsigned typRegalverwalter::AnzahlArtikelBestellbar (unsigned idnr, unsigned istv) const {
  unsigned anzTE = 0;
  for (LCursor<typTransportEinheit> c (ObjBestellFaehig); c; c++)
    if (c ()->hatArtikel (idnr, istv))  anzTE++;
  return anzTE;
}

/*virtual*/ double typRegalverwalter::MengeArbeitsplanBestellbar (unsigned idnr, unsigned istv) const {
  double sumMenge = 0.0;
  for (LCursor<typTransportEinheit> c (ObjBestellFaehig); c; c++)
    if (c ()->hatArbeitsplan (idnr, istv))  sumMenge += c ()->Menge ();
  return sumMenge;
}

/*virtual*/ double typRegalverwalter::MengeArtikelBestellbar (unsigned idnr, unsigned istv) const {
  double sumMenge = 0.0;
  for (LCursor<typTransportEinheit> c (ObjBestellFaehig); c; c++)
    if (c ()->hatArtikel (idnr, istv))  sumMenge += c ()->Menge ();
  return sumMenge;
}

/*virtual*/ typBaustein* typRegalverwalter::setVerwalter (typBaustein* newVerwalter) {
  if (newVerwalter->Basistyp () != tRegalverwalter)
    sim_error ("BAUSTEIN", "typRegalverwalter::setVerwalter",
               "Regalverwalter kann nur von Regalverwalter verwaltet werden", ObjNummer, FATAL);
  return (ObjVerwalter = (typRegalverwalter*) newVerwalter);
}

/*virtual*/ typRegalverwalter* typRegalverwalter::setBetreuer (typRegalverwalter* newBetreuer) {
  return (ObjBetreuer = newBetreuer);
}

typRegaleinheit* typRegalverwalter::Regal_zuweisen (typTransportEinheit *zTE) {
  typRegaleinheit* ziel = NULL;
  char buffer[255];
  sprintf(buffer, "strat_%i", ObjStrategie);
  switch (ObjStrategie) {
   case 0:   ziel = einlagern_Poolliste ();      break;
   case 1:   ziel = einlagern_gleichverteilt (); break;
   case 2:   auslagermelden ("POOLLISTE");      ziel = einlagern_Poolliste ();      break;
   case 3:   auslagermelden ("GLEICHVERTEILT"); ziel = einlagern_gleichverteilt (); break;
   case 4:   auslagermelden ("POOLLISTE");      ziel = einlagern_Poolliste ();      break;
   case 5:   auslagermelden ("GLEICHVERTEILT"); ziel = einlagern_gleichverteilt (); break;
   case 6:   ziel = einlagern_zielorientiert (zTE); break;
   default:  sim_error ("REGALVER", "Regal_zuweisen",
                        "Lagerstrategie existiert nicht", ObjStrategie, FATAL);
   /*< Rudi: 24-09-03: In der C-Schnittstelle wird eine Funktion gesucht, die strat_STRATEGIENUMMER heisst.
       Ist diese Funktion vorhanden, wird sie aufgerufen und der Variablen ziel zugewiesen. Ansonsten erfolgt
       der sim_error Aufruf. */
   /*default:  ziel = (typRegaleinheit*)PrgModul->callPrgFunc_typBaustein (buffer, zTE);
             if (!ziel){
                //Falsche Fehlermeldung
                //sim_error ("REGALVER", "Regal_zuweisen", "Lagerstrategie existiert nicht", ObjStrategie, FATAL);
                //sim_error ("REGALVER", "Regal_zuweisen", "Abbruch aufgrund eines Eingangsdatenfehlers. Kein Platz mehr in einem Segment", "", FATAL);
             }*/
  }
  return (ziel ? ziel : voll_Regal_zuweisen ());
}

typRegaleinheit* typRegalverwalter::voll_Regal_zuweisen () {
  int AvisVergleich = INT_MAX;
  typRegaleinheit* zZielRegal = ObjRegale.First ();
  for (; ObjRegale (); ObjRegale++)
    if ((!ObjRegale ()->isGesperrtOrGestoert ())
        && (AvisVergleich > ObjRegale ()->Avis ()->DirIstAnzahl ()))  
      AvisVergleich = (zZielRegal = ObjRegale ())->Avis ()->DirIstAnzahl ();
  return zZielRegal;
}

typRegaleinheit* typRegalverwalter::einlagern_Poolliste () {
  for (ObjRegale.First (); ObjRegale (); ObjRegale++)
    if (isZuweisbar (ObjRegale ()) && (!ObjRegale ()->isGesperrtOrGestoert ()))
      return ObjRegale ();
  return NULL;
}

typRegaleinheit* typRegalverwalter::einlagern_gleichverteilt () {
  unsigned KapVergleich = UINT_MAX;
  typRegaleinheit* zZielRegal = ObjRegale.First ();
  for (; ObjRegale (); ObjRegale++)
    if (isZuweisbar (ObjRegale ()) && (!ObjRegale ()->isGesperrtOrGestoert ())
        && (KapVergleich > (ObjRegale ()->Avis ()->DirIstAnzahl () +
                            ObjRegale ()->Belegung ())))
      KapVergleich = (zZielRegal = ObjRegale ())->Avis ()->DirIstAnzahl () +
                                   ObjRegale ()->Belegung ();
  return zZielRegal;
}

// Es wird bei Anfrage sofort ein freier Regalplatz gebucht 
typRegaleinheit* typRegalverwalter::einlagern_zielorientiert (typTransportEinheit *zTE) {
   double zielEntfernung = DBL_MAX;
   //LCursor<typVorgang> Vorgang = zTE->Vorgang();   
   //Vec3D zielPos = Vorgang.Next()->ZielBaustein()->GibGeoObject()->Pos();
   int nextVorgang = zTE->Vorgang()->Nummer()+1;
   Vec3D zielPos = zTE->Arbeitsplan()->findeVorgang(nextVorgang)->ZielBaustein()->GibGeoObject()->Pos();
   typRegaleinheit* zZielRegal = NULL; 
   for (ObjRegale.First (); ObjRegale (); ObjRegale++) {
      if (isZuweisbar (ObjRegale ()) && (!ObjRegale ()->isGesperrtOrGestoert ())) {
         Vec3D regalPos = ObjRegale()->GibGeoObject()->Pos();
         if (zielEntfernung > fabs((regalPos - zielPos).len())) {
            zZielRegal = ObjRegale();
            zielEntfernung = fabs((regalPos - zielPos).len());
         }
      }
   }        
   if (zZielRegal) {
      typPlatz* zZielPlatz = zZielRegal->waehlePlatz(zTE);
	   zTE->setBuchPlatz(zZielPlatz);
	   zTE->setZielPlatz(zZielPlatz);
   return zZielRegal;
}
   else {

      sim_error("REGALVER", "einlagern_zielorientiert", "", "", FATAL);  
   }
}

void typRegalverwalter::auslagermelden (char* str) {
  dprintf ("Auslagerungsstrategie nicht implementiert!\n");
  dprintf ("Auslagerung erfolgt nach %s\n", str);
}

bool typRegalverwalter::isZuweisbar (typRegaleinheit* zR) const {
 //return ((zR->Avis ()->DirIstAnzahl () ) < zR->Kapazitaet ());

 //return ((zR->Avis ()->DirIstAnzahl () + zR->Belegung ()) < zR->Kapazitaet ());

 //return ((zR->Avis ()->DirIstAnzahl () + 1) < zR->Kapazitaet ());

 return (zR->Reservierungen() < zR->Kapazitaet());
}

void typRegalverwalter::meldeBetreuTeil (const typTransportEinheit& w) {
  ObjBetreuTeile.append (w);
}

void typRegalverwalter::meldeBestellFaehig (const typTransportEinheit& w) {
  ObjBestellFaehig.append (w);
}

void typRegalverwalter::loescheBestellFaehig (const typTransportEinheit& w) {
  ObjBestellFaehig.remove (w);
}

DynArray<typRegaleinheit>& typRegalverwalter::gibRegale() {
   return ObjRegale;      
}
