// $Source: /usr/local/SimSoft/Sfx3D/src/gsl/regal.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:  $
// State assigned to revision
// $State: Exp $
//
//
// $Log: regal.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 "List.h"

#define _REGAL_CPP
#include "modell.h"
#include "regal.h"
#include <iostream.h>

#include "sfx.h"
#include "transp.h"
#include "baustein.h"
#include "regalver.h"
#include "drehpuff.h"
#include "ereignis.h"
#include "platz.h"
#include "arbplan.h"
#include "realzeit.h"

//---------------------------------------------------------
// class typRegaleinheit
//---------------------------------------------------------

typRegaleinheit::typRegaleinheit (unsigned n, unsigned b)
  : typBaustein      (n, b),
    ObjTransportEinheiten   (),
    ObjVerwalter     (NULL),
    ObjBetreuer      (NULL),
    ObjFachhoehe     (1.0),  //Fachhoehe ist Vorgabewert!!
    ObjBestellFaehig (),
    ObjArbeitsliste  (),
    ObjFaecherGeo    (2),
    ObjReservierungen (0) {
  ObjAvis = new typAvis();
}

typRegaleinheit::~typRegaleinheit () {
  while (ObjFaecherGeo.Last ())   delete ObjFaecherGeo.removeLast ();
  while (ObjArbeitsliste.Last ()) delete ObjArbeitsliste.removeLast ();
}

/*virtual*/ void typRegaleinheit::typTGSlesen (InputFile& file) {
  typBaustein::typTGSlesen (file);
  if (ObjPalPlaetze.Count () != 1)
    sim_error ("REGAL", "typRegaleinheit::typTGSlesen",
               "Regaleinheit mit != 1 Palettenplatz", ObjVariante, FATAL);
}

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

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

  tok.nextToken (file);
  ObjPhysKapazitaet = ObjKapazitaet = atoi (tok ());

  WrapBox box = ObjGeometry.WrapForParent ();
  //so ists besser, funktioniert egal wo der Nullpunkt des Regals ist!!  Norman 26.09.01
  ObjFachhoehe = fabs(box.Max.Z () - box.Min.Z());  
  GeoObject* geo = ObjGeometry.GObjArray ().First ();
  for (unsigned n = 1; n < ObjKapazitaet; n++) {
    Vec3D v (0.0, 0.0, (n * ObjFachhoehe));
    typPlatz *pl = new typPlatz (n, v + (ObjPalPlaetze [0]->Position () - ObjGeometry.Pos ()), this);
    ObjPalPlaetze [n] = pl;
    GeoObject *geoNew = new GeoObject ();
    geoNew->copyContents (*geo);
    ObjFaecherGeo.append (*geoNew);
    geoNew->DirToLocal (v);
    geoNew->move (v);
    geoNew->setParent (ObjGeometry);
    geoNew->setSelectionToMum ();
  }
}

/*virtual*/ typTransportEinheit *typRegaleinheit::TransportEinheit () const {
  //return ObjTransportEinheiten.First ();
  return ObjTransportEinheit;
}

/*virtual*/ typTransportEinheit *typRegaleinheit::gibTransportEinheit (typMeldung* zM) {
  typTransportEinheit *zTE = zM->TransportEinheit ();
  if (!ObjTransportEinheiten.remove (*zTE))
    sim_error ("BAUSTEIN", "typRegaleinheit::gibTransportEinheit",
               "falsches TransportEinheit angefordert", ObjNummer, FATAL);
  decBelegung ();
  if (ObjVerwalter) //Norman 01.04.02 4:26 Wer da bloss vergessen hat dass es auch Regaleinheiten ohne Verwalter gibt???
  {
  	ObjVerwalter->decBelegung ();
  }

  zTE->AktPlatz ()->setTransportEinheit (NULL);

  if (ObjVerwalter) //Norman 01.04.02 4:26 Wer da bloss vergessen hat dass es auch Regaleinheiten ohne Verwalter gibt???
  {
  	ObjVerwalter->Avis ()->decDirIstAnzahl ();
  }

  if (ObjBetreuer)
    ObjBetreuer->Anforderung_naechste_TE (this);

  planesofort (ETaabgeben, ObjNummer, zM->NachBaustein ()->Nummer ());
  return zTE;
}

void typRegaleinheit::a_aufnehmen () {
  typMeldung *zM = holeMeldung ();
  zM->setVonBaustein (this); // ?!? fuer was ist das ?!?
  ObjZielBaustein->gibTransportEinheit (zM);
  aufnehmenEinleiten (ObjTransportEinheit->ZielPlatz ()->Position (), ObjAufnahmezeit);

  if (ObjVerwalter) //Norman 01.04.02 4:26 Wer da bloss vergessen hat dass es auch Regaleinheiten ohne Verwalter gibt???
  {
  	ObjVerwalter->incBelegung ();
  }
}

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

  ObjTransportEinheiten.append (*ObjTransportEinheit);
  typBaustein* ziel = ObjTransportEinheit->Vorgang ()->ZielBaustein ();
  if ((ziel->Nummer () == ObjNummer) || (ziel->Nummer () == (ObjVerwalter ? ObjVerwalter->Nummer () : 0)))
    planeein (Modellzeit (), ETalagern, ObjNummer, 
              ObjTransportEinheit->AktPlatz ()->Nummer ());
  else
    if (ziel->Avis ()->isDirAvisOk ())
      ObjTransportEinheit->Anmelden ();
    else if (ObjVerwalter) //Norman 01.04.02 4:26 Wer da bloss vergessen hat dass es auch Regaleinheiten ohne Verwalter gibt???
    {
    	ObjVerwalter->meldeBetreuTeil (*ObjTransportEinheit);
    }

  ObjTransportEinheit = NULL;
  setZustand (RUHE);
  if (!ObjMeldungen.isEmpty ()) 
    planesofort (ETaktivieren, ObjNummer, ObjNummer);
  planesofort (ETeabgeben, ObjZielBaustein->Nummer (), ObjNummer);
}

void typRegaleinheit::a_lagern (int info) {
  planedelta(ObjPalPlaetze[(unsigned)info]->TransportEinheit()->Vorgang()->Dauer(),
              ETelagern, ObjNummer, info);
}

void typRegaleinheit::e_lagern (int info) {
   typTransportEinheit *zTE = ObjPalPlaetze[(unsigned)info]->TransportEinheit();
   int progNummer = zTE->Vorgang()->ProgNr();
   zTE->schalteVorgang();
   if (ObjVerwalter) {//Norman 01.04.02 4:26 Wer da bloss vergessen hat dass es auch Regaleinheiten ohne Verwalter gibt???
      ObjVerwalter->meldeBestellFaehig(*zTE);
   }
   ObjBestellFaehig.append(*zTE);
   if ((progNummer != -1) || WerkAusliefern(zTE)) {
      Ausliefern(zTE);
   }
   planesofort(ETaktivieren, ObjNummer, ObjNummer);
}

/*virtual*/ void typRegaleinheit::a_meldewarten () {
  sim_error ("REGAL", "typRegaleinheit::a_meldewarten",
             "a_meldewarten unzulaessig fuer Regaleinheiten", ObjNummer, FATAL);
}

/*virtual*/ void typRegaleinheit::a_abgeben () {
   ObjReservierungen--;
}

/*virtual*/ void typRegaleinheit::e_abgeben () {
  if (!ObjTransportEinheit) planesofort (ETaktivieren, ObjNummer, ObjNummer);
  aktiviereEingaenge ();
}

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

/*virtual*/ unsigned typRegaleinheit::setKapazitaet (unsigned wert) {
 if (wert > 0) {
 	
 	GeoObject* geo = ObjGeometry.GObjArray ().First ();
    
    
//    while (ObjFaecherGeo.First()) delete ObjFaecherGeo.First();
    
    for (unsigned n = 1; n < ObjKapazitaet; n++) {
    	delete ObjFaecherGeo.removeFirst();
      delete ObjPalPlaetze [n];
      ObjPalPlaetze [n] = NULL;
    }

    ObjPhysKapazitaet = ObjKapazitaet = wert;
  
  
 // ObjGeometry.invalidateWrap();
  WrapBox box = ObjGeometry.WrapForParent ();
  //so ists besser, funktioniert egal wo der Nullpunkt des Regals ist!!  Norman 26.09.01
  ObjFachhoehe = fabs(box.Max.Z () - box.Min.Z());  
  
  for (unsigned n = 1; n < ObjKapazitaet; n++) {
    Vec3D v (0.0, 0.0, (n * ObjFachhoehe));
    typPlatz *pl = new typPlatz (n, v + (ObjPalPlaetze [0]->Position () - ObjGeometry.Pos ()), this);
    ObjPalPlaetze [n] = pl;
    GeoObject *geoNew = new GeoObject ();
    geoNew->copyContents (*geo);
    ObjFaecherGeo.append (*geoNew);
    geoNew->DirToLocal (v);
    geoNew->move (v);
    geoNew->setParent (ObjGeometry);
    geoNew->setSelectionToMum ();
  }
  
    return ObjKapazitaet;
  }
  else
    sim_error ("typRegaleinheit", "setKapazitaet",
               "Kap muss > 0 sein", ObjNummer, FATAL);
  return 0;
}

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

/*virtual*/typBaustein* typRegaleinheit::setZielBaustein (typTransportEinheit *zTE) {
  typBaustein* ziel;

  if (ObjBetreuer && !(ObjAvis->isDirAvisOk ()))
    ziel = ObjBetreuer->setZielBaustein (zTE);
  else  {
    ziel = this;
    ObjAvis->decIndIstAnzahl ();
    ObjAvis->incDirIstAnzahl ();
    if (ObjVerwalter) //Norman 01.04.02 4:26 Wer da bloss vergessen hat dass es auch Regaleinheiten ohne Verwalter gibt???
    {
    	ObjVerwalter->Avis ()->incDirIstAnzahl ();
    }
    (zTE->Baustein ())->Avis ()->decDirIstAnzahl ();
  }
  return ziel;
}

/*virtual*/ typPlatz* typRegaleinheit::waehlePlatz (typTransportEinheit* zTE) {
  unsigned i;
  for (i = 0; i < ObjKapazitaet; i++){
    if (ObjPalPlaetze [i]->Buchung () == zTE){
    	 return ObjPalPlaetze [i];
    }
  }
  for (i = 0; i < ObjKapazitaet; i++)
    if (ObjPalPlaetze [i]->isVerfuegbar ())  break;
  if (i < ObjKapazitaet) {
    ObjPalPlaetze [i]->setBuchung (zTE);
    ObjBuchung++;
    return ObjPalPlaetze [i];
  }
  return NULL;
}

/*virtual*/ Vec3D typRegaleinheit::UebernahmePos (const typBaustein&) const {
  return (ObjPalPlaetze [0]->Position ());
}

/*virtual*/ Vec3D typRegaleinheit::UebergabePos (const typBaustein&) const {
  return (ObjPalPlaetze [0]->Position ());
}

void typRegaleinheit::Ausliefern (typTransportEinheit *zTE) {
  if (ObjVerwalter) //Norman 01.04.02 4:26 Wer da bloss vergessen hat dass es auch Regaleinheiten ohne Verwalter gibt???
  {
  	ObjVerwalter->loescheBestellFaehig (*zTE);
  }
  ObjBestellFaehig.remove (*zTE);
  zTE->Anmelden ();
}

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

unsigned typRegaleinheit::gibRegalverwalter() {
   if (ObjVerwalter) return ObjVerwalter->Nummer();   
   else return 0;
}

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

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

/*virtual*/ void typRegaleinheit::TEBestellung
                        (typTransportEinheit *te, unsigned sollvorg) {
   if (!te) {
      return (void) sim_error ("BAUSTEIN", "typRegaleinheit::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", "typRegaleinheit::TEBestellung",
               "TransportEinheit ist schon bestellt!", ObjNummer, FATAL);
         }
      }
      Bestellzettel *zBZ = new TEBestellzettel (te, sollvorg);
      ObjArbeitsliste.append (*zBZ);
      BestAusliefern (zBZ);
   }
}

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

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

/*virtual*/ bool typRegaleinheit::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;
}

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

/*virtual*/ typTransportEinheit* typRegaleinheit::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;
}

/*virtual*/ unsigned typRegaleinheit::AnzahlArbeitsplanVorhanden (unsigned arbNr, unsigned istv /*= 0*/) const {
  unsigned anzTE = 0;
  for (LCursor<typTransportEinheit> c (ObjTransportEinheiten); c; c++)
    if (c ()->hatArbeitsplan (arbNr, istv))  anzTE++;
  return anzTE;
}

/*virtual*/ unsigned typRegaleinheit::AnzahlArtikelVorhanden (unsigned artNr, unsigned istv /*= 0*/) const {
  unsigned anzTE = 0;
  for (LCursor<typTransportEinheit> c (ObjTransportEinheiten); c; c++)
    if (c ()->hatArtikel (artNr, istv))  anzTE++;
  return anzTE;
}

/*virtual*/ double typRegaleinheit::MengeArbeitsplanVorhanden (unsigned arbNr, unsigned istv /*= 0*/) const {
  double mengeTE = 0;
  for (LCursor<typTransportEinheit> c (ObjTransportEinheiten); c; c++)
    if (c ()->hatArbeitsplan (arbNr, istv))  mengeTE += c ()->Menge ();
  return mengeTE;
}

/*virtual*/ double typRegaleinheit::MengeArtikelVorhanden (unsigned artNr, unsigned istv /*= 0*/) const {
  double mengeTE = 0;
  for (LCursor<typTransportEinheit> c (ObjTransportEinheiten); c; c++)
    if (c ()->hatArtikel (artNr, istv))  mengeTE += c ()->Menge ();
  return mengeTE;
}

/*virtual*/ unsigned typRegaleinheit::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 typRegaleinheit::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 typRegaleinheit::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 typRegaleinheit::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 typRegaleinheit::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;
}

unsigned typRegaleinheit::ArbNrVonPlatz (unsigned platzNr) const {
  if (platzNr > ObjKapazitaet)
    sim_error ("REGAL", "ArbNrVonPlatz",
               "platzNr > Kapazitaet", ObjNummer, FATAL);
  return (ObjPalPlaetze[platzNr - 1]->TransportEinheit ()) ? 
    ObjPalPlaetze[platzNr - 1]->TransportEinheit ()->Arbeitsplan ()->Nummer () : 0;
}

unsigned typRegaleinheit::ArtNrVonPlatz (unsigned platzNr) const {
  if (platzNr > ObjKapazitaet)
    sim_error ("REGAL", "ArtNrVonPlatz",
               "platzNr > Kapazitaet", ObjNummer, FATAL);
  return (ObjPalPlaetze[platzNr - 1]->TransportEinheit ()) ? 
    ObjPalPlaetze[platzNr - 1]->TransportEinheit ()->Artikel () : 0;
}

unsigned typRegaleinheit::VorgNrVonPlatz (unsigned platzNr) const {
  if (platzNr > ObjKapazitaet)
    sim_error ("REGAL", "VorgNrVonPlatz",
               "platzNr > Kapazitaet", ObjNummer, FATAL);
  return (ObjPalPlaetze[platzNr - 1]->TransportEinheit ()) ? 
    ObjPalPlaetze[platzNr - 1]->TransportEinheit ()->Vorgang ()->Nummer () : 0;
}

/*virtual*/ void typRegaleinheit::individuellAktivieren (typBaustein* vonWem) {
  if ((ObjZustand == RUHE) || ((ObjZustand == WARTEDP) && (ObjZielBaustein == vonWem))) {
    typMeldung* zM = findeAusfuehrbareMeldung ();
    if (zM) {
      ObjTransportEinheit = zM->TransportEinheit ();
      ObjZielBaustein = zM->VonBaustein ();
      wartenaufnehmen (ObjTransportEinheit, ObjZielBaustein);
    }
  }
}

typMeldung* typRegaleinheit::findeAusfuehrbareMeldung () {
  for (LCursor<typMeldung> c (ObjMeldungen); c; c++) {
    typTransportEinheit* zTE = c ()->TransportEinheit ();
    typPlatz* p = waehlePlatz (zTE);
    if (p) {
      zTE->setZielPlatz (p);
      return &(ObjMeldungen.prepend (*(ObjMeldungen.remove (c))));
    }
  }
  return NULL;
}

bool typRegaleinheit::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; //Norman 01.04.02 4:26 Wer da bloss vergessen hat dass es auch Regaleinheiten ohne Verwalter gibt???
}

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

void typRegaleinheit::incReservierungen() {
   ObjReservierungen++;
}
void typRegaleinheit::decReservierungen() {
   ObjReservierungen--;
}

unsigned typRegaleinheit::Reservierungen() {
   return ObjReservierungen;
}

bool typRegaleinheit::BestellFaehig(typTransportEinheit *zTE) const {
   for (LCursor<typTransportEinheit> c (ObjBestellFaehig); c; c++) { 
      if (c () == zTE) return true;
   }
   return false;   
}
