<?php
/**
 * @copyright (c) Kufer Software GmbH, Oderstraße 7, D-84453 Mühldorf
 */

namespace Kufer\Web\KuferTools\Basis\AnwesenheitenKurs;

use Kufer\Web\Classes\Db\AnwesenheitFilter;
use Kufer\Web\Classes\db\searchobject\BelegungToObjectStatusByDate;
use Kufer\Web\Classes\Stream\ArrayStream;

abstract class AnwesenheitenUseCase extends \Kufer\Web\KuferTools\Basis\UseCase\EinzelKursUseCase
{
  public function __construct($request)
  {
    parent::__construct($request, new AnwesenheitenResponse());
  }

  public function action()
  {
    $zeitRaum = $this->getDatumErstenUndLetztenKurstag();
    $vonDat = $this->getVonDat($zeitRaum['vonDat']);
    $bisDat = $this->getBisDat($zeitRaum['bisDat']);
    $kursTageInfo = $this->getKursTageInfo($vonDat, $bisDat);
    $belegungInfo = $this->getBelegungInfo($vonDat, $bisDat);
    $belegungInfo = $this->addAdditionalInfoToBelegungInfo($belegungInfo, $vonDat, $bisDat);
    $verhaltenInfo = $this->getVerhaltenInfo($belegungInfo, $kursTageInfo);
    $anwesenheitsInfo = $this->getAnwesenheitsInfo($vonDat, $bisDat);

    $datamodulAry = $this->getDatamodulAry($kursTageInfo, $belegungInfo, $verhaltenInfo, $anwesenheitsInfo);

    return $this->getResponse($datamodulAry, $kursTageInfo);
  }

  private function getAnwColors()
  {
    $anwGruende = $this->getHLAnwesenheitGruende();

    $result = array();
    foreach ($anwGruende as $anwGrund) {
      $result[$anwGrund->status] = $anwGrund->farbe;
    }

    return $result;
  }

  private function mapAnwesenheitsInfo(
    ?BelegungToObjectStatusByDate $belegungToObjectStatusByDate,
    \AnwesenheitsKurstagInfo $kurstag,
    \Belegung $belegung,
    \Anwesenheit $anwesenheit
  ): object {
    $result = new class() extends \Anwesenheit {
      public $readonlyAustrittOderKuendigung = false;
    };

    foreach (get_object_vars($anwesenheit) as $var => $val) {
      $result->{$var} = $val;
    }

    if (!is_null($belegungToObjectStatusByDate) && !$belegungToObjectStatusByDate->isBelegungAllowed($belegung, $kurstag->datum)) {
      $result->readonlyAustrittOderKuendigung = true;
    }

    return $result;
  }

  private $cacheBelegungenById = array();

  private function findBelegungById(int $id): \Belegung
  {
    if (!isset($this->cacheBelegungenById[$id])) {
      $this->cacheBelegungenById[$id] = \DBWorkBelegung::getDefaultInstance()->getBelegung($id);
    }

    return $this->cacheBelegungenById[$id];
  }

  
  private function getDatamodulAry($kursTageInfo, $belegungInfo, $verhaltenInfo, $anwesenheitsInfo)
  {
    
    $anwInfoByBelId = \ArrayOperations::setAttributAsKeyArray($anwesenheitsInfo, 'abelegid');
    $belegungToObjectStatusByDate = BelegungToObjectStatusByDate::createFromSettings(
      \ToolSettingsAPI::getToolSettings(0),
      null
    );
    $result = array();
    foreach ($kursTageInfo as $ktInfo) {
      foreach ($belegungInfo as $belInfo) {
        $datamodul = new class() extends \Datamodul {
          public $ktInfo;
          public $belInfo;
          public $anwInfo;
          public $verhInfo;
          public $ignoreEintritt;
          public $ignoreAustritt;
        };

        $datamodul->ktInfo = $ktInfo;
        $datamodul->belInfo = $belInfo;

        $datamodul->anwInfo = new class() extends \Datamodul {
          public $readonlyAustrittOderKuendigung = false;
        }; 
        foreach (($anwInfoByBelId[$belInfo->belegid] ?? array()) as $anwInfo) {
          if ($anwInfo->aktid == $ktInfo->id) {
            $datamodul->anwInfo = $this->mapAnwesenheitsInfo(
              $belegungToObjectStatusByDate,
              $ktInfo,
              $this->findBelegungById($belInfo->belegid),
              $anwInfo
            );
          }
        }

        if (!($datamodul->anwInfo instanceof \Anwesenheit)
            && !is_null($belegungToObjectStatusByDate)) {
          $datamodul->anwInfo->readonlyAustrittOderKuendigung = !$belegungToObjectStatusByDate->isBelegungAllowed(
            $this->findBelegungById($belInfo->belegid),
            $ktInfo->datum
          );
        }

        $datamodul->verhInfo = new \Datamodul(); 
        foreach ($verhaltenInfo as $verhInfo) {
          if ($verhInfo->belegid == $belInfo->belegid
              && $verhInfo->ktid == $ktInfo->id) {
            $datamodul->verhInfo = $verhInfo;
            
            $datamodul->verhInfo->text = $verhInfo->text->text;
          }
        }

        $datamodul->ignoreAustritt = $this->shouldIgnoreAustrittsdatum();
        $datamodul->ignoreEintritt = $this->shouldIgnoreEintrittsdatum();

        if (!array_key_exists($belInfo->belegid, $result)) {
          $result[$belInfo->belegid] = array();
        }
        $result[$belInfo->belegid][$ktInfo->id] = $datamodul;
      }
    }

    return $result;
  }

  private function getDatumErstenUndLetztenKurstag()
  {
    static $bisDat = null;
    static $vonDat = null;

    if (is_null($bisDat)) {
      $dbwKTage = new \DBWorkKurseTageWT();
      if ($this->isDozTool()) {
        $dbwKTage->newSearch();
        $dbwKTage->toobject->nodoztool = true;
      }
      $tool0Settings = \ToolSettingsAPI::getToolSettings(0);
      $kurstageNurVonEingeloggtemDozent = $tool0Settings->kurstageNurVonEingeloggtemDozent && $this->isDozTool();
      $kurstagAry = $dbwKTage->getKurstagebyKursNR($this->request->knr, -1, $kurstageNurVonEingeloggtemDozent);
      $anzahlKurstage = count($kurstagAry);

      $bisDat = '';
      $vonDat = '';
      if ($anzahlKurstage > 0) {
        $firstKurstag = $kurstagAry[0];
        $lastKurstag = $kurstagAry[$anzahlKurstage - 1];
        $vonDat = $firstKurstag->datum;
        $bisDat = $lastKurstag->datum;
      }
    }

    return array('vonDat' => $vonDat, 'bisDat' => $bisDat);
  }

  
  private $cacheRestrictedZeitraumByFachbereich;

  private function isRestrictedZeitraumByFachbereich(): bool
  {
    if (is_null($this->cacheRestrictedZeitraumByFachbereich)) {
      $fachbereiche = array_filter(explode(':', $this->toolSettings->restrictKurstageForFachbereiche));
      $this->cacheRestrictedZeitraumByFachbereich = in_array($this->getKurs($this->getKursId())->fachb, $fachbereiche, true);
    }

    return $this->cacheRestrictedZeitraumByFachbereich;
  }

  private function getVonDat($pDefaultValue)
  {
    if (!is_null($this->request->vonDat) && $this->request->vonDat !== '') {
      $vonDat = $this->request->vonDat;
    }
    
    elseif (is_null($this->request->vonDat) && isset($this->toolSettings->anwFilterVonDatum) && $this->toolSettings->anwFilterVonDatum != '' && \DateTimeOperations::isValidDate($this->toolSettings->anwFilterVonDatum)) {
      $vonDat = $this->toolSettings->anwFilterVonDatum;
    } else {
      $vonDat = $pDefaultValue;
    }

    if ($this->isRestrictedZeitraumByFachbereich()) {
      switch ($this->toolSettings->restrictKurstageForFachbereicheZeitraum) {
        case 0:
          $today = \DateTimeOperations::getDateNow();
          $firstWeekday = \DateTimeOperations::getFirstDayOfWeek($today);
          $lastWeekday = \DateTimeOperations::getLastDayOfWeek($today);
          if (!\DateTimeOperations::isDateBetween($firstWeekday, $lastWeekday, $vonDat)) {
            $vonDat = $firstWeekday;
          }
          break;
        case 1:
          break;
        case 2:
          $today = \DateTimeOperations::getDateNow();
          $firstWeekday = \DateTimeOperations::getFirstDayOfWeek($today);
          if (\DateTimeOperations::isEmptyOrDefault($vonDat) || \DateTimeOperations::isDateBefore($vonDat, $firstWeekday)) {
            $vonDat = $firstWeekday;
          }
          break;
        case 3:
          $today = \DateTimeOperations::getDateNow();
          $firstDay = \DateTimeOperations::addDays($today, -$this->toolSettings->restrictKurstageForFachbereicheZeitraumDays);
          $lastDay = \DateTimeOperations::addDays($today, $this->toolSettings->restrictKurstageForFachbereicheZeitraumDays);
          if (!\DateTimeOperations::isDateBetween($firstDay, $lastDay, $vonDat)) {
            $vonDat = $firstDay;
          }
          break;
      }
    }

    return $vonDat;
  }

  private function getBisDat($pDefaultValue)
  {
    if (!is_null($this->request->bisDat) && $this->request->bisDat !== '') {
      $bisDat = $this->request->bisDat;
    } else {
      $bisDat = $pDefaultValue;
    }

    if ($this->isRestrictedZeitraumByFachbereich()) {
      switch ($this->toolSettings->restrictKurstageForFachbereicheZeitraum) {
        case 0:
          $today = \DateTimeOperations::getDateNow();
          $firstWeekday = \DateTimeOperations::getFirstDayOfWeek($today);
          $lastWeekday = \DateTimeOperations::getLastDayOfWeek($today);
          if (!\DateTimeOperations::isDateBetween($firstWeekday, $lastWeekday, $bisDat)) {
            $bisDat = $lastWeekday;
          }
          break;
        case 1:
          $today = \DateTimeOperations::getDateNow();
          $lastWeekday = \DateTimeOperations::getLastDayOfWeek($today);
          if (\DateTimeOperations::isEmptyOrDefault($bisDat) || \DateTimeOperations::isDateAfter($bisDat, $lastWeekday)) {
            $bisDat = $lastWeekday;
          }
          break;
        case 2:
          break;
        case 3:
          $today = \DateTimeOperations::getDateNow();
          $firstDay = \DateTimeOperations::addDays($today, -$this->toolSettings->restrictKurstageForFachbereicheZeitraumDays);
          $lastDay = \DateTimeOperations::addDays($today, $this->toolSettings->restrictKurstageForFachbereicheZeitraumDays);
          if (!\DateTimeOperations::isDateBetween($firstDay, $lastDay, $bisDat)) {
            $bisDat = $lastDay;
          }
          break;
      }
    }

    return $bisDat;
  }

  
  private function getKursTageInfo($vonDat, $bisDat)
  {
    $dbwAnw = new \DBWorkAnwesenheit();

    return $dbwAnw->getAnwesenheitKurstagInfo($this->request->knr, $vonDat, $bisDat, false, -1, $this->isDozTool());
  }

  
  private function getBelegungInfo($vonDat, $bisDat)
  {
    if ($this->shouldIgnoreAustrittsdatum()) {
      $vonDat = false;
    }
    if ($this->shouldIgnoreEintrittsdatum()) {
      $bisDat = false;
    }

    $dbwAnw = new \DBWorkAnwesenheit();

    $kurs = $this->getKurs($this->getKursId());
    $restrictStatusByDate = null;
    if (!\DateTimeOperations::isEmptyOrDefault($kurs->beginn_dat)) {
      $restrictStatusByDate = \Kufer\Web\Classes\db\searchobject\BelegungToObjectStatusByDate::createFromSettings(
        \ToolSettingsAPI::getToolSettings(0),
        $kurs->beginn_dat
      );
    }

    $anwesenheitFilter = new AnwesenheitFilter(
      $this->request->knr,
      $this->getTnStatusAry(),
      $vonDat,
      $bisDat,
      \ToolSettingsAPI::getToolSettings(0)->showKinderInAnwesenheiten5,
      false,
      $this->getFirmaTnr(),
      $this->getTeilnehmerTnr(),
      function(\DBWorkBelegung $dbwBelegung) use ($restrictStatusByDate): \DBWorkBelegung {
        $dbwBelegung->setPersonenOrderBySettings($this->toolSettings, 'tn');
        $dbwBelegung->toobject->restrictStatusByDate = $restrictStatusByDate;

        return $dbwBelegung;
      }
    );

    return $this->addFirmenInfo($dbwAnw->getAnwesenheitBelegungInfo($anwesenheitFilter));
  }

  
  private function addFirmenInfo(array $anwBelInfos): array
  {
    $dbwTn = \DBWorkTeilnehmer::getDefaultInstance(1, true);

    if (\DBWorkBASysSettings::isBadMergentheimFabi()) {
      
      $dbwBeziehungen = new \DBWorkBeziehung(1, true);

      return array_map(static function(\AnwesenheitsBelegungInfo $anwBelInfo) use ($dbwTn, $dbwBeziehungen) {
        $firmenBez = $dbwBeziehungen->getBeziehungMitarbeiterZuFirma($anwBelInfo->tnr);

        if (count($firmenBez) > 0) {
          $firma = $dbwTn->getTeilnehm($firmenBez[0]->nr1);
          $anwBelInfo->firma = $firma->getVarValue('name_ggf_vorname');
        }

        return $anwBelInfo;
      }, $anwBelInfos);
    } else {
      
      return array_map(static function(\AnwesenheitsBelegungInfo $anwBelInfo) use ($dbwTn) {
        $firmaTnr = $anwBelInfo->firmaId;
        if (!($firmaTnr > 0)) {
          return $anwBelInfo;
        } else {
          $firma = $dbwTn->getTeilnehm($firmaTnr);
          $anwBelInfo->firma = $firma->getVarValue('name_ggf_vorname');

          return $anwBelInfo;
        }
      }, $anwBelInfos);
    }
  }

  
  private function shouldIgnoreAustrittsdatum(): bool
  {
    return $this->toolSettings->tnIgnoreAustrittDate ?? false;
  }

  
  private function shouldIgnoreEintrittsdatum(): bool
  {
    return $this->toolSettings->tnIgnoreEintrittDate ?? false;
  }

  private function getVerhaltenInfo($belegungInfo, $ktageInfo)
  {
    $dbwAnw = new \DBWorkAnwesenheit();

    return $dbwAnw->getAnwesenheitsVerhaltenInfo($belegungInfo, $ktageInfo);
  }

  private function getAnwesenheitsInfo($vonDat, $bisDat)
  {
    $dbwAnw = new \DBWorkAnwesenheit();
    $anwInfos = $dbwAnw->getAnwesenheiten($this->request->knr, $vonDat, $bisDat, array(), -1, true, $this->getFirmaTnr());
    
    $hlAnwFarben = $this->getAnwColors();
    foreach ($anwInfos as $key => $anwInfo) {
      if (isset($hlAnwFarben[$anwInfo->astatus])) {
        $anwInfos[$key]->farbe = $hlAnwFarben[$anwInfo->astatus];
      }
      foreach ($anwInfo->_eanws as $key2 => $anwExtra) {
        $anwInfo->_eanws[$key2]->farbe = $hlAnwFarben[$anwExtra->estatus];
      }
    }

    return $anwInfos;
  }

  private function getTnStatusAry()
  {
    if (is_null($this->request->tnStatus)) {
      if ($this->toolSettings->anwFilterStatus == '') {
        return \StringOperations::str_split('APSZ'); 
      } else {
        return \StringOperations::str_split($this->toolSettings->anwFilterStatus);
      }
    }

    return $this->request->tnStatus;
  }

  private function showAnzeigeElement($pVarName)
  {
    $requestVar = $this->request->{'anzeige' . $pVarName};
    $toolSettingsVar = $this->toolSettings->{'anwFilterAnzeige' . $pVarName};

    
    return ((is_null($requestVar) && $toolSettingsVar) || (!is_null($requestVar)) && $requestVar);
  }

  private function showUhrzeit()
  {
    return $this->showAnzeigeElement('Uhrzeit');
  }

  private function showUE()
  {
    return $this->showAnzeigeElement('UE');
  }

  private function showDozent()
  {
    return $this->showAnzeigeElement('Dozent');
  }

  private function showFaecher()
  {
    return $this->showAnzeigeElement('Faecher');
  }

  private function showBemerkung()
  {
    return $this->showAnzeigeElement('Bemerkung');
  }

  private function addAdditionalInfoToBelegungInfo($pBelegungInfo, $pVonDat, $pBisDat)
  {
    $dbwAnw = new \DBWorkAnwesenheit(1, true);
    $showAnwInProzent = $this->toolSettings->useAnwInAnwKontr;
    $showAnwTage = $this->toolSettings->useAnwTageInAnwKontr;
    $showAbwTage = $this->toolSettings->useAbwInAnwKontr;
    if ($showAnwInProzent || $showAnwTage || $showAbwTage) {
      foreach ($pBelegungInfo as $belegung) {
        if ($showAnwInProzent) {
          $belegung->inProzent = $dbwAnw->getAnwProzent($belegung->belegid, $pVonDat, $pBisDat, $this->request->knr, '');
        }
        if ($showAnwTage) {
          $belegung->tageDa = $dbwAnw->getAnwTage($belegung->belegid, $pVonDat, $pBisDat, $this->request->knr, '');
        }
        if ($showAbwTage) {
          $belegung->tageWeg = $dbwAnw->getAbwTage($belegung->belegid, $pVonDat, $pBisDat, $this->request->knr, '');
        }
      }
    }

    return $pBelegungInfo;
  }

  protected function showFilterButton($sectId)
  {
    return $this->isRechtAllowed($sectId . '.0.2');
  }

  protected function getResponse($datamodulAry, $kursTageInfo)
  {
    
    $sectId = $this->getToolSectionId();
    $this->handleResponseStrategies();
    
    $this->response->kurs = $this->getKurs($this->request->knr);
    $this->response->belKtInfos = $datamodulAry;
    $this->response->kursTageInfo = $kursTageInfo;

    $this->response->showFilterButton = $this->showFilterButton($sectId);
    $this->response->zeigeUhrzeit = $this->showUhrzeit();
    $this->response->zeigeUE = $this->showUE();
    $this->response->zeigeDozent = $this->showDozent();
    $this->response->zeigeFaecher = $this->showFaecher();
    $this->response->zeigeBemerkung = $this->showBemerkung();
    $this->response->zeigeAnwInProzent = $this->toolSettings->useAnwInAnwKontr;
    $this->response->zeigeAnwTage = $this->toolSettings->useAnwTageInAnwKontr;
    $this->response->zeigeAbwTage = $this->toolSettings->useAbwInAnwKontr;
    $this->response->isBearbeitbar = $this->isBearbeitbar();
    $this->response->anwInZukunftBearbeitenErlaubt = $this->sindAnwInZukunftBearbeitbar();
    $this->response->anwVonAnderenDozentenBearbeitbar = $this->sindAnwVonAnderenDozentenBearbeitbar();

    if ($this->request->einstieg) {
      \Kufer\Web\KuferTools\Utils\Referrer::resetReferrerInSession($this->getToolId());
    } else {
      \Kufer\Web\KuferTools\Utils\Referrer::aktualisiereReferrerInSession($this->getToolId(), $this->request->referer, $this->request->refererLabel);
    }

    $this->response->backButtonLabel = $this->getBackButtonLabel();
    $this->response->backButtonLink = $this->getBackButtonLink($this->response->kurs->id, $this->request->belegId);

    $this->response->anwGruende = $this->getHLAnwesenheitGruende();
    $this->response->anwGruendeLegende = $this->getHLAnwesenheitGruendeLegende();
    $this->response->bearbeiterKuferTools = \ToolSettingsAPI::getToolSettings(0)->direktMaKuerzel;
    $this->response->anwesenheitenAusKuferSQLNichtBearbeitbar = $this->areAnwesenheitenAusKuferSQLNichtBearbeitbar();

    $this->response->setFilter = $this->request->setFilter;

    $this->response->xsrf = \Xsrf::getXsrfGetParameter('kurs_anw_aendern', $this->response->kurs->id);
    $this->response->statusMessageContainer = $this->request->statusMessageContainer;

    return $this->response;
  }

  protected function areAnwesenheitenAusKuferSQLNichtBearbeitbar(): bool
  {
    return false;
  }

  
  protected function getHLAnwesenheitGruende()
  {
    static $cache = null;
    if (is_null($cache)) {
      $dbw = \DBWorkHLAnwesenheitGruende::getDefaultInstance();
      $cache = $dbw->getAll();
    }

    return $cache;
  }

  
  protected function getHLAnwesenheitGruendeLegende(): array
  {
    $statusAry = array_filter(explode(',', $this->toolSettings->anwShowLegendeStatus));

    return (new ArrayStream($this->getHLAnwesenheitGruende()))
      ->filter(static function(\HLAnwesenheitGruende $grund) use ($statusAry): bool {
        if (empty($statusAry)) {
          return true; 
        } else {
          return in_array($grund->status, $statusAry);
        }
      })
      ->toArray();
  }

  protected function getBackButtonLabel()
  {
    return \Kufer\Web\KuferTools\Utils\Referrer::getReferrerLabelFromSession($this->getToolId(), 'Zurück');
  }

  protected function getBackButtonLink($pKnr, $pBelegId)
  {
    $defaultLink = 'index.php?tool_id=' . $this->getToolId() . '&action=kursdetails&knr=' . $pKnr . ($pBelegId != '' ? '&belid=' . $pBelegId : '');

    return \Kufer\Web\KuferTools\Utils\Referrer::getReferrerFromSession($this->getToolId(), $defaultLink);
  }

  protected function getResponseStrategies()
  {
    return array(
        new \Kufer\Web\KuferTools\Utils\ResponseStrategies\ResponseMitRahmen($this)
    );
  }

  protected function isBearbeitbar()
  {
    return false;
  }

  protected function sindAnwInZukunftBearbeitbar()
  {
    return false;
  }

  protected function sindAnwVonAnderenDozentenBearbeitbar()
  {
    return false;
  }

  protected function getFirmaTnr()
  {
    return null;
  }

  
  protected function getTeilnehmerTnr()
  {
    return null;
  }

  protected function isDozTool()
  {
    return false;
  }
} 

class AnwesenheitenRequest
{
  public $einstieg;
  public $knr;
  public $belegId;
  public $vonDat;
  public $bisDat;
  public $tnStatus;
  public $anzeigeUhrzeit;
  public $anzeigeUE;
  public $anzeigeDozent;
  public $anzeigeFaecher;
  public $anzeigeBemerkung;
  public $referer;
  public $refererLabel;
  public $statusMessageContainer;

  public $setFilter = false;
}

class AnwesenheitenResponse extends \Kufer\Web\KuferTools\Basis\UseCase\ToolResponse
{
  public $user;
  public $menuPunkte;
  public $kurs;
  public $belKtInfos;
  public $kursTageInfo;
  public $showFilterButton;
  public $zeigeUhrzeit;
  public $zeigeUE;
  public $zeigeDozent;
  public $zeigeFaecher;
  public $zeigeBemerkung;
  public $zeigeAnwInProzent;
  public $zeigeAnwTage;
  public $zeigeAbwTage;
  public $backButtonLabel;
  public $backButtonLink;
  public $xsrf;
  public $isBearbeitbar = false;
  public $statusMessageContainer;
  public $anwInZukunftBearbeitenErlaubt;
  public $anwVonAnderenDozentenBearbeitbar;
  public $anwGruende;
  public $anwGruendeLegende;
  public $bearbeiterKuferTools;
  public $anwesenheitenAusKuferSQLNichtBearbeitbar;
  public $setFilter;
}