<?php
/**
 * @copyright (c) Kufer Software GmbH, Loisachstrasse 4, D-84453 Mühldorf
 */

namespace Kufer\KuferTools\Utils\GebaeudeGrafik;

use Kufer\KuferTools\Utils\DatamodulHelper\KursorteHelper\KursorteHelper;
use Kufer\Web\KuferTools\Utils\FilterSearch\KursorteUebersichtSearchFilter;

class GebauedeGrafik
{
  public $dbwKurstage;
  public $dbwKursorte;
  public $dbwOrtVerf;
  public $dateTimeOp;

  public $toolId;
  public $toolSettings;

  public $startTime;
  public $endTime;
  public $datVon;
  public $dateForm;

  public $markieren;
  public $user;

  
  public $filterGebaeude;

  
  protected $gebaeude;

  public function __construct($toolId, $toolSettings, $startTime, $endTime, $datVon, $dateForm, $markieren, $user, $filterGebaeude = null)
  {
    $this->dbwKurstage = new \DBWorkKurseTage();
    $this->dbwKursorte = new \DBWorkKursorte();
    $this->dbwOrtVerf = new \DBWorkOrtVerf();
    $this->dateTimeOp = new \DateTimeOperations();

    $this->toolId = $toolId;
    $this->toolSettings = $toolSettings;
    $this->startTime = $startTime;
    $this->endTime = $endTime;
    $this->datVon = $datVon;
    $this->dateForm = $dateForm;
    $this->markieren = $markieren;
    $this->user = $user;

    $this->filterGebaeude = $filterGebaeude;

    $this->setDBWFromToObjs();
  }

  private function setDBWFromToObjs()
  {
    $this->dbwKurstage->newSearch();
    $this->dbwOrtVerf->newSearch();

    $datVon = $this->getDatumVon();

    if (\DateTimeOperations::isValidDate($datVon)) {
      $this->dbwKurstage->fromobject->datum = $datVon;
      $this->dbwKurstage->toobject->datum = $datVon;
      $this->dbwOrtVerf->fromobject->vondat = $datVon;
      $this->dbwOrtVerf->toobject->bisdat = $datVon;
    }
  }

  public function isRechtAllowed($pRecht)
  {
    return \SecurityOperations::privilegAllowedCurrentUser($this->toolId, $pRecht) >= 1;
  }

  private function validateDate($date)
  {
    $d = \DateTime::createFromFormat('d.m.Y', $date);

    return $d && $d->format('d.m.Y') === $date;
  }

  private function getTimeStepsCount()
  {
    return ($this->getTableEndTime() - $this->getTableStartTime()) / $this->getTimeInterval();
  }

  private function getTableStartTime()
  {
    if ($this->startTime != '') {
      return floor($this->dateTimeOp->getTimeStringasFloatUpperBound($this->startTime));
    } else {
      if (\DBWorkBASysSettings::isIHK()) {
        return 7;
      }

      return 8;
    }
  }

  private function getTableEndTime()
  {
    if ($this->endTime != '') {
      return round($this->dateTimeOp->getTimeStringasFloatLowerBound($this->endTime));
    } else {
      if (\DBWorkBASysSettings::isIHK()) {
        return 22;
      }

      return 23;
    }
  }

  private function getTimeInterval()
  {
    return 1;
  }

  private function getDatumVon()
  {
    $datVon = $this->datVon;
    if (empty($datVon)) {
      $datVon = $this->dateTimeOp->getDateNow();
    }
    if (!empty($this->dateForm)) {
      switch ($this->dateForm) {
        case DATE_TAG_VOR:
          return $this->dateTimeOp->getNextDate($datVon, 1);
        case DATE_TAG_ZUR:
          return $this->dateTimeOp->getNextDate($datVon, -1);
        case DATE_WO_VOR:
          return $this->dateTimeOp->getNextDate($datVon, 7);
        case DATE_WO_ZUR:
          return $this->dateTimeOp->getNextDate($datVon, -7);
      }
    }

    return $datVon;
  }

  private function getTimeSteps()
  {
    $beginnTime = $this->getTableStartTime();
    $timestepsCount = $this->getTimeStepsCount();
    $timesteps = array();
    if ($timestepsCount < 0.1) { 
      $timestepsCount = 1;
    }
    for ($i = 0; $i < $timestepsCount; $i++) {
      $timesteps[$i] = array();
      $timesteps[$i]['time_string'] = \DateTimeOperations::getFloatasTimeString($beginnTime, true);
      $timesteps[$i]['time_string_bis'] = \DateTimeOperations::getFloatasTimeString($beginnTime + $this->getTimeInterval(), true);
      $timesteps[$i]['kurstag'] = 0;
      $timesteps[$i]['sperrtag'] = 0;

      $beginnTime += $this->getTimeInterval();
    }

    return $timesteps;
  }

  public function isFirstKurstag($pKtag)
  {
    return $pKtag->datum == $pKtag->kurs->beginn_dat;
  }

  private function getTimeStepObj($timestepAry)
  {
    $tabellenZelle = new TabellenZelle();
    $tabellenZelle->vonZeit = $timestepAry['time_string'];
    $tabellenZelle->bisZeit = $timestepAry['time_string_bis'];

    return $tabellenZelle;
  }

  public function addSperrkopie($ind, $day, $getov, $ortv, &$pSperrary, &$pIndary)
  {
    $sperrkopie = $getov->getNewObject();
    $sperrkopie->id = $ortv->id;
    $sperrkopie->ortnr = $ortv->ortnr;
    $sperrkopie->wochentag = $ortv->wochentag;
    $sperrkopie->vondat = $day;
    $sperrkopie->bisdat = $day;
    $sperrkopie->vonuhr = $ortv->vonuhr;
    $sperrkopie->bisuhr = $ortv->bisuhr;
    $sperrkopie->verantwort = $ortv->verantwort;
    $sperrkopie->begruendung = $ortv->begruendung;
    $sperrkopie->bearbeiter = $ortv->bearbeiter;
    $sperrkopie->bearbdatum = $ortv->bearbdatum;
    $pSperrary[] = $sperrkopie;
    $pIndary[] = $ind;
  }

  private function getBeginTime($startime4grafikbox)
  {
    return max(\DateTimeOperations::getTimeStringasFloat($startime4grafikbox), $this->getTableStartTime());
  }

  private function getEndTime($stoptime4grafikbox)
  {
    return min($this->getTableEndTime(), \DateTimeOperations::getTimeStringasFloat($stoptime4grafikbox));
  }

  public function isEndTimeLessThanBeginnTime($beginnTime, $endTime)
  {
    $btime = $this->getBeginTime($beginnTime);
    $etime = $this->getEndTime($endTime);

    return $etime <= $btime; 
  }

  public function getAktuelleURLRelativeMitParams()
  {
    $datVon = $this->getDatumVon();
    $uhrVon = \DateTimeOperations::getFloatasTimeString($this->getTableStartTime(), true);
    $uhrBis = \DateTimeOperations::getFloatasTimeString($this->getTableEndTime(), true);

    $url = \URL::loadCurrent();

    return $url->getUrlRelative() . '&datVon=' . $datVon . '&starttime=' . $uhrVon . '&endtime=' . $uhrBis;
  }

  public function getWidth($beginnTime, $endTime)
  {
    $btime = $this->getBeginTime($beginnTime);
    $etime = $this->getEndTime($endTime);
    if ($etime <= $btime) {
      return 0;
    }
    $panelsteps = ($etime - $btime) / $this->getTimeInterval();

    return round((float)$panelsteps * 100);
  }

  private function getFarbeLegendsAry()
  {
    $farbeLegends = array();
    foreach ($this->toolSettings as $setting => $val) {
      if (\StringOperations::startsWith($setting, 'farbe')) {
        $farbeLegends[str_replace(array('farbe', 'norm'), '', $setting)] = $val;
      }
    }

    return $farbeLegends;
  }

  public const DEFAULT_COLOR = 'WHITE';

  private function getLegends4Bel()
  {
    $kurstage = $this->getKurstage4Gebaeude();
    $legendsArry = array();

    $farbeLegends = $this->getFarbeLegendsAry();
    $defsett = new \ToolRaumUebersichtSettings();
    $artlegendary = $defsett->legend_arry['art'];

    $sperrlegend = count($this->getSperrArray()) > 0;
    $showKursplanungLegende = \DBWorkBASysSettings::isGarchingVhs() && count($this->getKursplanungArray()) > 0;

    if (count($kurstage) == 0 && !$sperrlegend && !$showKursplanungLegende) {
      return array();
    }
    
    $vork_ary = \array_unique(\ArrayOperations::mapAttribut($kurstage, 'ausfall'));
    foreach ($vork_ary as $vork_wert) {
      if ($farbeLegends[$vork_wert] != self::DEFAULT_COLOR) {
        $farbeLegendObj = new \stdClass();
        $farbeLegendObj->color = $farbeLegends[$vork_wert];
        $farbeLegendObj->zustand = $artlegendary[$vork_wert];
        $legendsArry[] = $farbeLegendObj;
      }
    }
    if ($sperrlegend && $farbeLegends['S'] != self::DEFAULT_COLOR) {
      $sperrFarbeLegend = new \stdClass();
      $sperrFarbeLegend->color = $farbeLegends['S'];
      $sperrFarbeLegend->zustand = $artlegendary['S'];
      $legendsArry[] = $sperrFarbeLegend;
    }
    if ($this->containsFirstKT($kurstage) && $farbeLegends['1K'] != self::DEFAULT_COLOR) {
      $firstKtFarbeLegend = new \stdClass();
      $firstKtFarbeLegend->color = $farbeLegends['1K'];
      $firstKtFarbeLegend->zustand = $artlegendary['1K'];
      $legendsArry[] = $firstKtFarbeLegend;
    }
    if ($showKursplanungLegende) {
      $kursplanungFarbeLegend = new \stdClass();
      $kursplanungFarbeLegend->color = $farbeLegends['Kursplanung'];
      $kursplanungFarbeLegend->zustand = $artlegendary['Kursplanung'];
      $legendsArry[] = $kursplanungFarbeLegend;
    }

    $ruestzeitLegend = \ArrayOperations::any($kurstage, function(\Kurstag $kurstag) {
      return $kurstag->rzvor > 0 || $kurstag->rznach > 0;
    });
    if ($ruestzeitLegend && $farbeLegends[self::COLORKEY_RZVOR] != self::DEFAULT_COLOR) {
      $kursplanungFarbeLegend = new \stdClass();
      $kursplanungFarbeLegend->color = $farbeLegends[self::COLORKEY_RZVOR];
      $kursplanungFarbeLegend->zustand = $artlegendary[self::COLORKEY_RZVOR];
      $legendsArry[] = $kursplanungFarbeLegend;
    }
    if ($ruestzeitLegend && $farbeLegends[self::COLORKEY_RZNACH] != self::DEFAULT_COLOR) {
      $kursplanungFarbeLegend = new \stdClass();
      $kursplanungFarbeLegend->color = $farbeLegends[self::COLORKEY_RZNACH];
      $kursplanungFarbeLegend->zustand = $artlegendary[self::COLORKEY_RZNACH];
      $legendsArry[] = $kursplanungFarbeLegend;
    }

    return $legendsArry;
  }

  
  public function setGebaeude(?array $gebaeude): void
  {
    $this->gebaeude = $gebaeude;
  }

  private function getFilteredGebaeude(): ?array
  {
    if (!is_null($this->filterGebaeude) && !empty($this->filterGebaeude)) {
      return $this->filterGebaeude;
    } else {
      return null;
    }
  }

  private function getGebaeude($user)
  {
    if (!is_null($this->gebaeude)) {
      return $this->gebaeude;
    }

    $gebaeude = array();

    $searchApi = new KursorteUebersichtSearchFilter();
    $suchdm = $searchApi->getSearchMapFromSession($this->toolId);
    $tmpGeb = array_map(static function($str) {
      return $str === '-' ? '' : $str;
    }, array_filter((array)($suchdm['gebaeude'] ?? array())));
    if (!empty($tmpGeb)) {
      $gebaeude = $tmpGeb;
    } else {
      $dbwKursortGeb = new \DBWorkKursortGebaeude();
      $dbwKursortGeb->newSearch();

      $nurZugeordneteOrte = false;
      if (isset($this->toolSettings->nurzugeordneteorte)) {
        $nurZugeordneteOrte = $this->toolSettings->nurzugeordneteorte;
      }

      if (isset($this->toolSettings->nurRaeumeAussenst) && $this->toolSettings->nurRaeumeAussenst == true) {
        $dbwUser = new \DBWorkBASysUser();
        $BASysUser = $dbwUser->getBASysUser($this->user->ben);
        $dbwKursortGeb->fromobject->aussenstelle = $BASysUser->aussenstelle;
      }

      $kursortgeb = $dbwKursortGeb->getGebaeudeByUserID($user->id, $nurZugeordneteOrte, !$this->toolSettings->raumNichtVerfuegbar);
      $filterGebaeude = $this->getFilteredGebaeude();
      foreach ($kursortgeb as $kgeb) {
        if ((is_null($filterGebaeude) || \in_array($kgeb->schule, $filterGebaeude))) {
          $gebaeude[] = $kgeb->schule;
        }
      }
    }

    return $gebaeude;
  }

  
  private $kursorteCache;

  private function getKursorte()
  {
    if (is_null($this->kursorteCache)) {
      $gebaeude = $this->getGebaeude($this->user);

      $kursortHelper = KursorteHelper::createKursorteGebaeudeGrafik($this->user, $this->toolSettings, $gebaeude);

      $markIds = $this->markieren->updateAndGetIdsFromSessionAsArray();
      if ($this->toolSettings->markierungenbelegungsplan && count($markIds) > 0) {
        $this->kursorteCache = $kursortHelper->getDatamoduleByIds($markIds);
      } else {
        $this->kursorteCache = $kursortHelper->getDatamoduleOhneFilter();
      }
    }

    return $this->kursorteCache;
  }

  public function getKurse()
  {
    $kurstage = $this->getKurstage4Gebaeude();
    $knrAry = \ArrayOperations::mapAttribut($kurstage, 'knr');
    $dbwKurse = new \DBWorkKurseWT(1, true);
    $dbwKurse->beratungstageKurseAusblenden = false;
    $kurse = $dbwKurse->getKurseByKursnummern($knrAry);

    return \ArrayOperations::setAttributAsKey($kurse, 'id');
  }

  private function getSperrtage4Raumzeile($sperrtage, $raumsteps)
  {
    foreach ($sperrtage as $sperrtag) {
      foreach ($raumsteps as $raumstep) {
        foreach ($raumstep->tabellenZellen as &$tabellenZelle) {
          if ($this->liegtSperrtagInTimestep($sperrtag, $tabellenZelle, $raumstep)) {
            \array_push($tabellenZelle->tabellenEintraege, ...Tabelleneintrag::getEintragBySperrzeit($sperrtag, $tabellenZelle, $this));
          }
        }
      }
    }

    return $raumsteps;
  }

  private function liegtZeitraumInTimestep($beginn, $ende, $kursortId, $tabellenZelle, $tabellenZeile)
  {
    if ($kursortId == $tabellenZeile->kursort->id) {
      $beginnZeitraum = \DateTimeOperations::getTimeStringasFloat($beginn);
      $beginnTime = \DateTimeOperations::getTimeStringasFloat($tabellenZelle->vonZeit);
      $endTime = $beginnTime + $this->getTimeInterval();

      if (($beginnZeitraum >= $beginnTime && $beginnZeitraum < $endTime)
        || ($beginnTime == $this->getTableStartTime()
        && $beginnZeitraum < $this->getTableStartTime()
        && \DateTimeOperations::getTimeStringasFloat($ende) > $this->getTableStartTime())) {
        return true;
      }
    }

    return false;
  }

  private function liegtSperrtagInTimestep($sperrtag, $tabellenZelle, $tabellenZeile)
  {
    if ($sperrtag->wochentag == 0 || $sperrtag->wochentag == $this->dateTimeOp->getWeekDayFromDate($this->getDatumVon())) {
      return $this->liegtZeitraumInTimestep($sperrtag->vonuhr, $sperrtag->bisuhr, $sperrtag->ortnr, $tabellenZelle, $tabellenZeile);
    }

    return false;
  }

  private function liegtKurstagInTimestep($kurstag, $tabellenZelle, $tabellenZeile)
  {
    return $this->liegtZeitraumInTimestep($kurstag->zeit_beg, $kurstag->zeit_ende, $kurstag->kursort->id, $tabellenZelle, $tabellenZeile);
  }

  private function liegtKursplanungInTimestep($kursplanung, $tabellenZelle, $tabellenZeile)
  {
    $kursort = $this->dbwKursorte->getKursortbymktext($kursplanung->kursort);

    return $this->liegtZeitraumInTimestep($kursplanung->beginn, $kursplanung->ende, $kursort->id, $tabellenZelle, $tabellenZeile);
  }

  private function getZeitspalten()
  {
    $timesteps = $this->getTimeSteps();
    $result = array();
    foreach ($timesteps as $timestep) {
      $result[] = $this->getTimeStepObj($timestep);
    }

    return $result;
  }

  public function getSperrfarbe()
  {
    $farbeLegends = $this->getFarbeLegendsAry();

    return $farbeLegends['S'];
  }

  private function containsFirstKT($kurstage)
  {
    $that = $this;

    return \ArrayOperations::in_array_u(true, $kurstage, function($true, $kurstag) use ($that) {
      return $that->isFirstKurstag($kurstag);
    });
  }

  private function getKurstage4Raumzeile($kurstage, $raumsteps)
  {
    foreach ($raumsteps as $raumstep) {
      foreach ($raumstep->tabellenZellen as $tabellenZelle) {
        foreach ($kurstage as $kurstag) {
          if ($this->liegtKurstagInTimestep($kurstag, $tabellenZelle, $raumstep)) {
            \array_push($tabellenZelle->tabellenEintraege, ...Tabelleneintrag::getEintragByKurstag($kurstag, $tabellenZelle, $this));
          }
        }
      }
    }

    return $raumsteps;
  }

  public function getFarbeForKT($kurstag)
  {
    $farbeLegends = $this->getFarbeLegendsAry();
    if ($this->isFirstKurstag($kurstag)) {
      $farbe = $farbeLegends['1K'];
    } else {
      $farbe = $farbeLegends[$kurstag->ausfall];
    }
    if ($farbe == 'WHITE') {
      $farbe = 'YELLOW';
    }

    return $farbe;
  }

  public const COLORKEY_RZVOR = 'Aufruestungszeit';
  public const COLORKEY_RZNACH = 'Abruestungszeit';

  public function getFarbeForAufruestungszeit()
  {
    $farbeLegends = $this->getFarbeLegendsAry();

    return $farbeLegends[self::COLORKEY_RZVOR];
  }

  public function getFarbeForAbruestungszeit()
  {
    $farbeLegends = $this->getFarbeLegendsAry();

    return $farbeLegends[self::COLORKEY_RZNACH];
  }

  public function getFarbeForKursplanung()
  {
    $farbeLegends = $this->getFarbeLegendsAry();

    return $farbeLegends['Kursplanung'];
  }

  
  public function getHoeheForZeile()
  {
    $hoehe = '50.5px';
    
    $cellHasMin3Entries = ($this->toolSettings->medienanzeigen1 && $this->toolSettings->showDozentInGebaeudeGrafik
        && $this->toolSettings->showKursNrInGebaeudeGrafik)
      || ($this->toolSettings->medienanzeigen1 && $this->toolSettings->showDozentInGebaeudeGrafik && $this->toolSettings->showKursTitelInGebaeudeGrafik)
      || ($this->toolSettings->medienanzeigen1 && $this->toolSettings->showKursNrInGebaeudeGrafik && $this->toolSettings->showKursTitelInGebaeudeGrafik)
      || ($this->toolSettings->showDozentInGebaeudeGrafik && $this->toolSettings->showKursNrInGebaeudeGrafik && $this->toolSettings->showKursTitelInGebaeudeGrafik);
    if ($cellHasMin3Entries) {
      $hoehe = '55px';
    }
    if ($this->toolSettings->medienanzeigen1 && $this->toolSettings->showDozentInGebaeudeGrafik
        && $this->toolSettings->showKursNrInGebaeudeGrafik && $this->toolSettings->showKursTitelInGebaeudeGrafik) {
      $hoehe = '72px';
    }

    return $hoehe;
  }

  private function getKursplanungen4Raumzeile($kursplanungen, $raumsteps)
  {
    foreach ($kursplanungen as $kursplanung) {
      foreach ($raumsteps as $raumstep) {
        foreach ($raumstep->tabellenZellen as &$tabellenZelle) {
          if ($this->liegtKursplanungInTimestep($kursplanung, $tabellenZelle, $raumstep)) {
            \array_push($tabellenZelle->tabellenEintraege, ...Tabelleneintrag::getEintragByKursplanung($kursplanung, $tabellenZelle, $this));
          }
        }
      }
    }

    return $raumsteps;
  }

  public function setRaumPanelSettings()
  {
    $raumzeilen = $this->getRaumzeilen();
    $kurstage = $this->getKurstage4Gebaeude();

    if (!empty($kurstage)) {
      $raumzeilen = $this->getKurstage4Raumzeile($kurstage, $raumzeilen);
    }

    $sperrtage = $this->getSperrArray();
    if (!empty($sperrtage)) {
      $raumzeilen = $this->getSperrtage4Raumzeile($sperrtage, $raumzeilen);
    }

    if (\DBWorkBASysSettings::isGarchingVhs()) {
      $kursplanungen = $this->getKursplanungArray();
      if (!empty($kursplanungen)) {
        $raumzeilen = $this->getKursplanungen4Raumzeile($kursplanungen, $raumzeilen);
      }
    }

    return $raumzeilen;
  }

  private function getSperrArray()
  {
    static $cache = null;
    if (is_null($cache)) {
      $raumNummern = \ArrayOperations::mapAttribut($this->getKursorte(), 'id');
      $cache = $this->dbwOrtVerf->getKursorteVerfByRaumNummern($raumNummern);
    }

    return $cache;
  }

  public function getKurstage4Gebaeude()
  {
    static $cache = null;
    if (is_null($cache)) {
      $raumNummern = \ArrayOperations::mapAttribut($this->getKursorte(), 'id');
      $cache = $this->dbwKurstage->getKurstageByRaeumeIds($raumNummern);
    }

    return $cache;
  }

  private function getKursplanungArray()
  {
    static $cache = null;
    if (is_null($cache)) {
      $dbwKursplanung = \DBWorkKursplanung::getDefaultInstance();

      $raumBezeichnungen = \ArrayOperations::mapAttribut($this->getKursorte(), 'mktext');
      $datumVon = $this->getDatumVon();
      $datumBis = $datumVon;

      $kursplanungen = $dbwKursplanung->getKursplanungen4Grafiken($raumBezeichnungen, $datumVon, $datumBis);

      $anzahlKursplanungen = count($kursplanungen);
      for ($i = 0; $i < $anzahlKursplanungen; $i++) {
        $days = \DateTimeOperations::getDaysFromTo($datumVon, $kursplanungen[$i]->beginn_dat) - 1;
        
        if ($kursplanungen[$i]->kurstage_anlegen && $days % 7 !== 0) {
          unset($kursplanungen[$i]);
        }
      }

      $cache = $kursplanungen;
    }

    return $cache;
  }

  private function getRaumzeilen()
  {
    $raumzeilen = array();
    $kursorte = $this->getKursorte();
    $tabellenZellen = $this->getZeitspalten();
    foreach ($kursorte as $kursort) {
      $raumzeile = new TabellenZeile();
      $raumzeile->tabellenZellen = array_map(function($spalte) {return clone $spalte;}, $tabellenZellen); 
      $raumzeile->kursort = $kursort;
      $raumzeile->vonZeit = $this->getTableStartTime();
      $raumzeile->bisZeit = $this->getTableEndTime();
      
      $raumzeile->height = $this->getHoeheForZeile();

      $raumzeilen[] = $raumzeile;
    }

    return $raumzeilen;
  }

  public function containsDataToDisplay(): bool
  {
    $kursorte = $this->getKursorte();
    if ($this->validateDate($this->getDatumVon())) {
      return true;
    }
    if (!($this->getTimeStepsCount() <= 0 || count($kursorte) <= 0 || $this->getTableStartTime() >= $this->getTableEndTime())) {
      return true;
    }

    return false;
  }

  public function fillResponseObject($response, $user)
  {
    $allgSettings = \AllgSettingsAPI::getAllgSettings();
    $gebaeude = array_map(static function($str) {
      if ($str === '') {
        return 'Ohne Gebäude';
      } else {
        return $str;
      }
    }, $this->getGebaeude($user));
    $response->gebaeude = implode(', ', $gebaeude);
    $response->filterGebaeude = $this->getFilteredGebaeude() ?? array();

    $response->vhsname = $allgSettings->vhsname;
    $response->wkAndDate = \DateTimeOperations::getWeekDay($this->getDatumVon()) . ', ' . $this->getDatumVon();
    $response->datVon = $this->getDatumVon();
    $response->uhrVon = \DateTimeOperations::getFloatasTimeString($this->getTableStartTime(), true);
    $response->uhrBis = \DateTimeOperations::getFloatasTimeString($this->getTableEndTime(), true);
    $response->timesteps = $this->getTimeSteps();
    $response->toolID = $this->toolId;
    $response->farbeLegends = $this->getLegends4Bel();
    if (!empty($response->farbeLegends)) {
      $response->farbeLegendsHeader = 'Farblegende:';
    }

    return $response;
  }

  public function getStartTimeAsFloat(): float
  {
    return \DateTimeOperations::getTimeStringasFloat($this->startTime);
  }

  public function getEndTimeAsFloat(): float
  {
    return \DateTimeOperations::getTimeStringasFloat($this->endTime);
  }
}

class TabellenZeile
{
  public $vonZeit;
  public $bisZeit;

  public $beschriftung;
  public $tabellenZellen = array();
  public $height;
}

class TabellenZelle
{
  public $vonZeit;
  public $bisZeit;
  public $tabellenEintraege = array();
}

class Tabelleneintrag extends \Datamodul
{
  public $zeit_beg;
  public $zeit_ende;
  public $kurstag;
  public $farbe;

  
  public $tabellenZelle;

  
  public $gebaudeGrafik;
  public $isruestungszeit = false;
  public $text = '';

  public function getVarValue($varname)
  {
    switch (\StringOperations::strtolower($varname)) {
      default:
        return parent::getVarValue($varname);
      case 'left':
        $diff = \max($this->gebaudeGrafik->getStartTimeAsFloat(), \DateTimeOperations::getTimeStringasFloat($this->zeit_beg)) - \DateTimeOperations::getTimeStringasFloat($this->tabellenZelle->vonZeit);
        $whole = floor($diff);
        $fraction = $diff - $whole;

        return round((float)$fraction * 100);
      case 'width':
        return $this->gebaudeGrafik->getWidth($this->zeit_beg, $this->zeit_ende);
      case 'left_exakt':
        $diff = \max(\DateTimeOperations::convertTimeToMinutes($this->gebaudeGrafik->startTime), \DateTimeOperations::convertTimeToMinutes($this->zeit_beg)) - \DateTimeOperations::convertTimeToMinutes($this->tabellenZelle->vonZeit);

        return (($diff / 60.0) * 100);
      case 'width_exakt':
        return $this->gebaudeGrafik->getWidth($this->zeit_beg, $this->zeit_ende);
    }
  }

  public static function getEintragByKurstag($kurstag, $tabellenZelle, $gebaudeGrafik): array
  {
    $result = array();
    if ($kurstag->rzvor > 0) {
      $result[] = self::getEinzelEintragByKurstag(
        $kurstag,
        self::addRuestungszeit($kurstag->zeit_beg, -$kurstag->rzvor),
        $kurstag->zeit_beg,
        $kurstag->rzvorinfo,
        $tabellenZelle,
        $gebaudeGrafik,
        true,
        $gebaudeGrafik->getFarbeForAufruestungszeit()
      );
    }
    $result[] = self::getEinzelEintragByKurstag($kurstag, $kurstag->zeit_beg, $kurstag->zeit_ende, '', $tabellenZelle, $gebaudeGrafik);
    if ($kurstag->rznach > 0) {
      $result[] = self::getEinzelEintragByKurstag(
        $kurstag,
        $kurstag->zeit_ende,
        self::addRuestungszeit($kurstag->zeit_ende, $kurstag->rznach),
        $kurstag->rznachinfo,
        $tabellenZelle,
        $gebaudeGrafik,
        true,
        $gebaudeGrafik->getFarbeForAbruestungszeit()
      );
    }

    return $result;
  }

  protected static function addRuestungszeit($uhrzeit, $minuten): string
  {
    return \DateTimeOperations::convertMinutesToTime(\max(\DateTimeOperations::convertTimeToMinutes($uhrzeit) + $minuten, 0) % (24 * 60));
  }

  public static function getEinzelEintragByKurstag($kurstag, $zeit_beg, $zeit_ende, $text, $tabellenZelle, $gebaudeGrafik, $isruestung = false, $farbe = ''): Tabelleneintrag
  {
    $tabellenEintrag = new Tabelleneintrag();
    $tabellenEintrag->zeit_beg = $zeit_beg;
    $tabellenEintrag->zeit_ende = $zeit_ende;
    $tabellenEintrag->kurstag = $kurstag;
    $tabellenEintrag->kurs = $kurstag->kurs;
    $tabellenEintrag->dozent = $kurstag->dozent ?? $kurstag->kurs->hauptdozent;
    $tabellenEintrag->farbe = !$isruestung ? $gebaudeGrafik->getFarbeForKT($kurstag) : $farbe;
    $tabellenEintrag->isruestungszeit = $isruestung;
    $tabellenEintrag->text = $text;

    $tabellenEintrag->tabellenZelle = $tabellenZelle;
    $tabellenEintrag->gebaudeGrafik = $gebaudeGrafik;

    return $tabellenEintrag;
  }

  public static function getEintragBySperrzeit($sperrzeit, $tabellenZelle, $gebaudeGrafik): array
  {
    $result = array();
    $result[] = self::getEinzelEintragBySperrzeit($sperrzeit, $sperrzeit->vonuhr, $sperrzeit->bisuhr, $tabellenZelle, $gebaudeGrafik);

    return $result;
  }

  public static function getEinzelEintragBySperrzeit($sperrzeit, $vonuhr, $bisuhr, $tabellenZelle, $gebaudeGrafik): Tabelleneintrag
  {
    $tabellenEintrag = new Tabelleneintrag();
    $tabellenEintrag->zeit_beg = $vonuhr;
    $tabellenEintrag->zeit_ende = $bisuhr;
    $tabellenEintrag->kurstag = $sperrzeit;
    $tabellenEintrag->farbe = $gebaudeGrafik->getSperrfarbe();

    $tabellenEintrag->tabellenZelle = $tabellenZelle;
    $tabellenEintrag->gebaudeGrafik = $gebaudeGrafik;

    return $tabellenEintrag;
  }

  public static function getEintragByKursplanung($kursplanung, $tabellenZelle, $gebaudeGrafik): array
  {
    $result = array();
    $result[] = self::getEinzelEintragByKursplanung($kursplanung, $kursplanung->beginn, $kursplanung->ende, $tabellenZelle, $gebaudeGrafik);

    return $result;
  }

  public static function getEinzelEintragByKursplanung($kursplanung, $beginn, $ende, $tabellenZelle, $gebaudeGrafik): Tabelleneintrag
  {
    $tabellenEintrag = new Tabelleneintrag();
    $tabellenEintrag->zeit_beg = $beginn;
    $tabellenEintrag->zeit_ende = $ende;
    $tabellenEintrag->kurstag = $kursplanung;
    $tabellenEintrag->farbe = $gebaudeGrafik->getFarbeForKursplanung();

    $tabellenEintrag->tabellenZelle = $tabellenZelle;
    $tabellenEintrag->gebaudeGrafik = $gebaudeGrafik;

    return $tabellenEintrag;
  }
}