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

namespace Kufer\Web\KuferTools\Basis\Raumplanung\KurstageVorschau;

use Kufer\Web\KuferTools\Basis\Raumplanung\BerechneKurstage;

abstract class KurstageVorschauUseCase extends \Kufer\Web\KuferTools\Basis\UseCase\ToolUseCase
{
  private $kursHelper;

  public const ERROR_VORSCHAU_SPERRZEITEN_DOPPELBELEGUNG = 'errorVorschauSperrzeitenDoppelbelegung';
  public const ERROR_VORSCHAU_KEINE_KURSTAGE = 'errorVorschauKeineKurstage';

  public function __construct($request)
  {
    parent::__construct($request, new KurstageVorschauResponse());
    $this->kursHelper = \Kufer\Web\KuferTools\Utils\DatamodulHelper\Kurse\KursHelperFactory::getKursHelperByToolID($this->getToolId(), $this->toolSettings);
  }

  public function hasAccess($pToolSectionId = null)
  {
    if (!parent::hasAccess()) {
      return false;
    }
    if (!$this->request->isXsrfOk && !$this->request->isRefresh) {
      return false;
    }
    if (!$this->isRaumplanungAllowed()) {
      return false;
    }
    if (!in_array($this->request->knr, $this->kursHelper->getIdsOhneFilter())) {
      return false;
    }
    if ($this->request->ktDnr !== '' && !$this->isDozentImKurs()) {
      return false;
    }

    return true;
  }

  abstract protected function isRaumplanungAllowed(): bool;

  private function getKurs(): \Kurs
  {
    return $this->kursHelper->getDatamodulById($this->request->knr);
  }

  private function isDozentImKurs(): bool
  {
    $dbwDozHon = new \DBWorkDozentenHonorar(1, true);

    return $dbwDozHon->getHonorardatenCount($this->request->knr, $this->request->ktDnr) > 0;
  }

  
  public function action()
  {
    $kurs = $this->getKurs();

    $kurstage = \SessionOperations::getVarValueWTIntern('raumplanungKurstageAnzeige', null);
    if (!isset($kurstage)) {
      try {
        $kurstage = $this->berechneKurstageDatum($kurs);
        $kurstage = $this->extendKurstageWithKurs($kurs, $kurstage);
        $kurstage = $this->extendKurstageWithDozenten($kurs, $kurstage);
        $this->setKurstageToSession($kurstage);
        $kurstage = $this->extendKurstageWithStatus($kurstage);
        $this->setKurstageAnzeigeToSession($kurstage);
        if (count($kurstage) === 0) {
          $this->response->showRaumplanungFormular = true;
          $this->setFormularDatenToResponse();
          $this->setStatusMeldungen(self::ERROR_VORSCHAU_KEINE_KURSTAGE);
        }
      } catch (\Exception $e) {
        \Logger::logg('Raumplanung: Prüfung auf Sperrzeiten und Doppelbelegung - folgende Exception ist aufgetreten: ' . $e->getMessage() . ' [' . $e->getTraceAsString() . ']', 200);
        $this->response->showRaumplanungFormular = true;
        $this->setFormularDatenToResponse();
        $this->setStatusMeldungen(self::ERROR_VORSCHAU_SPERRZEITEN_DOPPELBELEGUNG);
      }
    }

    return $this->getResponse($kurs, $kurstage);
  }

  private function getEingestellteWochentage(): array
  {
    return array_map(function($wochentagName) {
      switch ($wochentagName) {
        case 'montag': return 1;
        case 'dienstag': return 2;
        case 'mittwoch': return 3;
        case 'donnerstag': return 4;
        case 'freitag': return 5;
        case 'samstag': return 6;
        case 'sonntag': return 7;
        default: return 0; 
      }
    }, $this->request->wochentage);
  }

  private function berechneKurstageDatum($pKurs): array
  {
    $berechner = $this->getKurstageBerechner($pKurs);
    $datumArray = $berechner->getTage();

    return $this->getKurstage4DatumsArray($datumArray);
  }

  
  private function getKurstage4DatumsArray(array $datumsArray): array
  {
    return array_map(array($this, 'getKurstag4Datum'), $datumsArray);
  }

  
  private function extendKurstageWithKurs(\Kurs $pKurs, array $pKurstage): array
  {
    return array_map(function($kurstag) use ($pKurs) {
      $kurstag->kurs = $pKurs;

      return $kurstag;
    }, $pKurstage);
  }

  
  private function extendKurstageWithDozenten(\Kurs $pKurs, array $pKurstage): array
  {
    if (empty($pKurstage)) {
      return $pKurstage;
    }

    $dbwDozenten = new \DBWorkDozenten(1, true);
    $dozenten = $dbwDozenten->getDozenten4Kurs($pKurs->id);

    return array_map(function($kurstag) use ($dozenten) {
      foreach ($dozenten as $dozent) {
        if ($kurstag->dnr == $dozent->id) {
          $kurstag->dozent = $dozent;
          break;
        }
      }

      return $kurstag;
    }, $pKurstage);
  }

  
  private function extendKurstageWithStatus(array $pKurstage): array
  {
    if (empty($pKurstage)) {
      return $pKurstage;
    }

    if (\SessionOperations::isDmzBetrieb()) {
      $abgleichAPI = new \AbgleichAPI();

      $kurstageNode = new \XMLNode('kurstage');

      $checkDoppelbelegungNode = new \XMLNode('check_doppelbelegung', $this->request->checkDoppelbelegung ? 'W' : 'F');
      $checkSperrzeitenNode = new \XMLNode('check_sperrzeiten', $this->request->checkSperrzeiten ? 'W' : 'F');

      $kurstageNode->addChild($checkDoppelbelegungNode);
      $kurstageNode->addChild($checkSperrzeitenNode);
      foreach ($pKurstage as $kurstag) {
        $kurstagNode = new \XMLNode('kurstag');
        $kurstagNode->addChild(new \XMLNode('knr', $kurstag->knr));
        $kurstagNode->addChild(new \XMLNode('dnr', $kurstag->dnr));
        $kurstagNode->addChild(new \XMLNode('ort', $kurstag->ort));
        $kurstagNode->addChild(new \XMLNode('datum', $kurstag->datum));
        $kurstagNode->addChild(new \XMLNode('zeit_beg', $kurstag->zeit_beg));
        $kurstagNode->addChild(new \XMLNode('zeit_ende', $kurstag->zeit_ende));

        $kurstageNode->addChild($kurstagNode);
      }

      $result = $abgleichAPI->sendeNachricht(
        \AbgleichAPI::ABGLEICH_CHECK_KURSTAGE_RAUMPLANUNG,
        new \Nutzlast($kurstageNode)
      );
      $kurstagInfos = \JsonOperations::getFromString($result->reason);

      $pKurstage = $this->setStatusRaumAndChecked($pKurstage, $kurstagInfos);
    } else { 
      foreach ($pKurstage as $kurstag) {
        $kurstag->statusRaum = '';
        $kurstag->checked = false;

        $ueberschneidungSperrzeiten = false;
        $doppelbelegungRaum = false;

        $doppelterKurstag = $this->checkDoppelterKurstag($kurstag->knr, $kurstag->dnr, $kurstag->ort, $kurstag->datum, $kurstag->zeit_beg, $kurstag->zeit_ende);
        if ($this->request->checkSperrzeiten) {
          $ueberschneidungSperrzeiten = $this->checkSperrzeiten($kurstag->ort, $kurstag->datum, $kurstag->zeit_beg, $kurstag->zeit_ende);
        }
        if (!$doppelterKurstag && $this->request->checkDoppelbelegung) {
          $doppelbelegungRaum = $this->checkDoppelbelegung($kurstag->ort, $kurstag->datum, $kurstag->zeit_beg, $kurstag->zeit_ende);
        }

        $kurstag->statusRaum = $this->getStatusRaum4Kurstag($doppelterKurstag, $doppelbelegungRaum, $ueberschneidungSperrzeiten);
        $kurstag->checked = !($kurstag->statusRaum != '');
      }
    }

    return $pKurstage;
  }

  private function getKurstag4Datum(string $datum): \Kurstag
  {
    $kurstag = new \Kurstag();
    $kurstag->knr = $this->request->knr;
    $kurstag->setValue2Var('datum', $datum);
    $kurstag->zeit_beg = $this->request->ktUhrzeitVon;
    $kurstag->zeit_ende = $this->request->ktUhrzeitBis;
    $kurstag->pause = $this->request->ktPause;

    
    
    \KurstagAPI::aktualisiereKurstagZeit($kurstag);

    $kurstag->ort = $this->request->ktOrt;
    $kurstag->dnr = $this->request->ktDnr;
    $kurstag->bemerkungen = $this->request->ktBemerkung;
    $kurstag->bestuhlung = $this->request->ktBestuhlung;

    return $kurstag;
  }

  private function getDbwOrtVerf(): \DBWorkOrtVerf
  {
    static $dbwOrtVerf = null;
    if (is_null($dbwOrtVerf)) {
      $dbwOrtVerf = new \DBWorkOrtVerf(1, true);
    }

    return $dbwOrtVerf;
  }

  private function getDbwKursorte(): \DBWorkKursorte
  {
    static $dbwKursorte = null;
    if (is_null($dbwKursorte)) {
      $dbwKursorte = new \DBWorkKursorte(1, true);
    }
    $dbwKursorte->newSearch();

    return $dbwKursorte;
  }

  private function getDbwKurseTage(): \DBWorkKurseTage
  {
    static $dbwKursetage = null;
    if (is_null($dbwKursetage)) {
      $dbwKursetage = new \DBWorkKurseTage(1, true, true, false);
      $dbwKursetage->newSearch();
      
      $dbwKursetage->fromobject->ausfall = 'A';
    }

    return $dbwKursetage;
  }

  
  private function checkDoppelterKurstag(string $pKnr, int $pDozId, string $pKursort, string $pDatum, string $pUhrzeitVon, string $pUhrzeitBis): bool
  {
    $dbwKurseTage = $this->getDbwKurseTage();
    $dbwKurseTage->fromobject->ort = $pKursort;
    $dbwKurseTage->fromobject->datum = $pDatum;
    $dbwKurseTage->toobject->datum = $pDatum;
    $kurstage = $dbwKurseTage->getKurstageByKursNrAndDozentNr($pKnr, $pDozId);

    foreach ($kurstage as $kurstag) {
      if ($kurstag->ort == $pKursort && $pUhrzeitVon == $kurstag->zeit_ende && $pUhrzeitBis == $kurstag->zeit_beg) {
        return true;
      }
    }

    return false;
  }

  
  private function getSperrzeiten(string $pKursort): array
  {
    $sperrzeiten = array();
    $dbwKursorte = $this->getDbwKursorte();
    $kursort = $dbwKursorte->getKursortbymktext($pKursort);

    if ($kursort->id > 0) {
      $dbwOrtVerf = $this->getDbwOrtVerf();
      $sperrzeiten = $dbwOrtVerf->getKursorteVerfbyRaumDat($kursort->id);
    }

    return $sperrzeiten;
  }

  
  private function checkSperrzeiten(string $pKursort, string $pDatum, string $pUhrzeitVon, string $pUhrzeitBis): bool
  {
    $sperrzeiten = $this->getSperrzeiten($pKursort);

    foreach ($sperrzeiten as $sperrzeit) {
      if (\DateTimeOperations::isDateAfter($pDatum, \DateTimeOperations::addDays($sperrzeit->vondat, -1))
        && \DateTimeOperations::isDateBefore($pDatum, \DateTimeOperations::addDays($sperrzeit->bisdat, 1))
        && (
          ($pUhrzeitVon <= $sperrzeit->vonuhr && $pUhrzeitBis > $sperrzeit->vonuhr)
          || ($pUhrzeitVon < $sperrzeit->bisuhr && $pUhrzeitBis >= $sperrzeit->bisuhr)
          || ($pUhrzeitVon >= $sperrzeit->vonuhr && $pUhrzeitBis <= $sperrzeit->bisuhr)
        )
      ) {
        return true;
      }
    }

    return false;
  }

  
  private function getKurstageGleichesDatum(string $pKursort, string $pDatum): array
  {
    $kurstage = array();
    $dbwKursorte = $this->getDbwKursorte();
    
    $dbwKursorte->fromobject->doppel = 'Nein';
    $kursort = $dbwKursorte->getKursortbymktext($pKursort);

    if ($kursort->id > 0) {
      $dbwKurseTage = $this->getDbwKurseTage();
      $dbwKurseTage->fromobject->datum = $pDatum;
      $dbwKurseTage->toobject->datum = $pDatum;

      $kurstage = $dbwKurseTage->getKurstagebyRaum($kursort->id);
    }

    return $kurstage;
  }

  private function checkDoppelbelegung(string $pKursort, string $pDatum, string $pUhrzeitVon, string $pUhrzeitBis): bool
  {
    $kurstage = $this->getKurstageGleichesDatum($pKursort, $pDatum);

    foreach ($kurstage as $kurstag) {
      if (\DateTimeOperations::checkTimeCollisions($pUhrzeitVon, $pUhrzeitBis, $kurstag->zeit_beg, $kurstag->zeit_ende)) {
        return true;
      }
    }

    return false;
  }

  private function getStatusRaum4Kurstag(bool $doppelterKurstag, bool $doppelbelegungRaum, bool $ueberschneidungSperrzeiten): string
  {
    $status = array();

    if ($doppelterKurstag) {
      $status[] = 'doppelter Kurstag';
    } elseif ($doppelbelegungRaum) {
      $status[] = 'Überschneidung mit einem anderen Kurstag';
    }

    if ($ueberschneidungSperrzeiten) {
      $status[] = 'Überschneidung mit Sperrzeiten des Raumes';
    }

    return implode('<br />', $status);
  }

  
  private function setStatusRaumAndChecked(array $pKurstage, array $pKurstagInfos): array
  {
    foreach ($pKurstage as $kurstag) {
      foreach ($pKurstagInfos as $kurstagInfo) {
        if ($kurstag->datum == $kurstagInfo->Datum) {
          $kurstag->statusRaum = $this->getStatusRaum4Kurstag($kurstagInfo->DoppelterKurstag, $kurstagInfo->DoppelbelegungRaum, $kurstagInfo->UeberschneidungSperrzeiten);
          $kurstag->checked = !($kurstag->statusRaum != '');
        }
      }
    }

    return $pKurstage;
  }

  private function getKurstageBerechner(\Kurs $pKurs): BerechneKurstage\BerechneKurstage
  {
    $endeDat = !empty($pKurs->ende_dat) ? $pKurs->ende_dat : DEFAULTDATE;

    if ($this->request->rhythmus === 'alle14tage') {
      return new BerechneKurstage\BerechneKurstage14Taegig($this->request->anfangsdatum, $this->request->anzahlTermine, $this->getEingestellteWochentage(), $endeDat);
    } elseif ($this->request->rhythmus === 'monatlich') {
      return new BerechneKurstage\BerechneKurstageMonatlich($this->request->anfangsdatum, $this->request->anzahlTermine, $this->getEingestellteWochentage(), $endeDat);
    } else {
      return new BerechneKurstage\BerechneKurstageWoechentlich($this->request->anfangsdatum, $this->request->anzahlTermine, $this->getEingestellteWochentage(), $endeDat);
    }
  }

  
  private function setKurstageToSession(array $pKurstage): void
  {
    $kurstage = array_map(function($kurstag) { return clone($kurstag); }, $pKurstage);
    \SessionOperations::setVarValueWTIntern('raumplanungKurstage', $kurstage);
  }

  
  private function setKurstageAnzeigeToSession(array $pKurstage): void
  {
    \SessionOperations::setVarValueWTIntern('raumplanungKurstageAnzeige', $pKurstage);
  }

  private function setStatusMeldungen(string $statusCode): void
  {
    $statusMessage = null;
    if ($statusCode === self::ERROR_VORSCHAU_SPERRZEITEN_DOPPELBELEGUNG) {
      $statusMessage = \Kufer\Web\KuferTools\Utils\StatusMessages\PlainStatusMessage::getErrorMessageObj(
        'Bei der Prüfung auf Sperrzeiten und Doppelbelegung gab es einen Fehler. Bitte versuchen Sie es erneut oder wenden Sie sich an die Einrichtung.'
      );
    } elseif ($statusCode === self::ERROR_VORSCHAU_KEINE_KURSTAGE) {
      $statusMessage = \Kufer\Web\KuferTools\Utils\StatusMessages\PlainStatusMessage::getErrorMessageObj(
        'Mit diesen Einstellungen würden keine Termine angelegt werden. Bitte prüfen und korrigieren Sie Ihre Angaben.'
      );
    }

    if (isset($statusMessage)) {
      $this->response->statusMeldungen->addStatusMessageObj($statusMessage);
    }
  }

  private function setFormularDatenToResponse(): void
  {
    $this->response->formularDaten = array();
    $this->response->formularDaten['anfangsdatum'] = $this->request->anfangsdatum;
    $this->response->formularDaten['rhythmus'] = $this->request->rhythmus;
    $this->response->formularDaten['wochentage'] = $this->request->wochentage;
    $this->response->formularDaten['anzahlTermine'] = $this->request->anzahlTermine;
    $this->response->formularDaten['checkDoppelbelegung'] = $this->request->checkDoppelbelegung;
    $this->response->formularDaten['checkSperrzeiten'] = $this->request->checkSperrzeiten;
    $this->response->formularDaten['ktUhrzeitVon'] = $this->request->ktUhrzeitVon;
    $this->response->formularDaten['ktUhrzeitBis'] = $this->request->ktUhrzeitBis;
    $this->response->formularDaten['ktPause'] = $this->request->ktPause;
    $this->response->formularDaten['ktPause'] = $this->request->ktPause;
    $this->response->formularDaten['ktPause'] = $this->request->ktPause;
    $this->response->formularDaten['ktOrt'] = $this->request->ktOrt;
    $this->response->formularDaten['ktDnr'] = $this->request->ktDnr;
    $this->response->formularDaten['ktBemerkung'] = $this->request->ktBemerkung;
    $this->response->formularDaten['ktBestuhlung'] = $this->request->ktBestuhlung;
  }

  
  protected function getResponse(\Kurs $pKurs, array $pKurstage): KurstageVorschauResponse
  {
    $this->handleResponseStrategies();
    $this->response->kurs = $pKurs;
    $this->response->kurstage = $pKurstage;

    return $this->response;
  }

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

class KurstageVorschauRequest
{
  public $knr = '';

  public $anfangsdatum = '';
  public $rhythmus = '';
  public $wochentage = array();
  public $anzahlTermine = '';
  public $checkDoppelbelegung = true;
  public $checkSperrzeiten = true;

  public $ktDatumVon = '';
  public $ktDatumBis = '';
  public $ktUhrzeitVon = '';
  public $ktUhrzeitBis = '';
  public $ktPause = '';
  public $ktOrt = '';
  public $ktDnr = '';
  public $ktBemerkung = '';
  public $ktBestuhlung = '';
}

class KurstageVorschauResponse extends \Kufer\Web\KuferTools\Basis\UseCase\ToolResponse
{
  public $user;
  public $menuPunkte;
  public $kurs;
  public $kurstage;
  public $showRaumplanungFormular = false;
  public $formularDaten;
}