src/Manager/SeatPlanManager.php line 215

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Manager;
  4. use App\Entity\Local\Icon;
  5. use App\Entity\Local\OrderSeat;
  6. use App\Entity\Local\Response\SeatPlanResponse;
  7. use App\Entity\Local\Response\SetTicketsOnServerResponse;
  8. use App\Entity\Local\SetTicketsForSessionRequest;
  9. use App\Entity\Local\ValueObject\ThirdPartyMemberScheme;
  10. use App\Entity\Vista\DTO\TicketDTO;
  11. use App\Entity\Vista\LoyaltyMember;
  12. use App\Entity\Vista\Order;
  13. use App\Entity\Vista\Row;
  14. use App\Entity\Vista\Seat;
  15. use App\Entity\Vista\SeatPosition;
  16. use App\Entity\Vista\Session;
  17. use App\Entity\Vista\SessionTicketType;
  18. use App\Entity\Vista\Ticket;
  19. use App\Entity\Vista\TicketDetails;
  20. use App\Exceptions\SeatPlanException;
  21. use App\Repository\CinemaAreaCategoryRepository;
  22. use App\Repository\OrderRepositoryInterface;
  23. use App\Repository\SeatPlanRepository;
  24. use App\Repository\TicketRepository;
  25. use App\Security\Security;
  26. use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
  27. use Symfony\Component\Yaml\Yaml;
  28. use Symfony\Contracts\Translation\TranslatorInterface;
  29. use Psr\Log\LoggerInterface;
  30. use App\Repository\RestrictionsRepository;
  31. use App\Entity\Local\Restrictions;
  32. use App\Repository\Vista\SessionVistaRepository as SessionRepository;
  33. use App\Repository\SeatPlanLocalRepository;
  34. use Exception;
  35. use Symfony\Component\Serializer\SerializerInterface;
  36. use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
  37. /**
  38.  * @TODO: split in 2-3 services
  39.  */
  40. class SeatPlanManager
  41. {
  42.     const FREE_STATUSES = [0399];
  43.     const FREE_STATUSES_NO_WHEELCHAIR = [099];
  44.     const OCCUPIED_STATUSES = [14];
  45.     /**
  46.      * @var SeatPlanRepository
  47.      */
  48.     protected $seatPlanRepository;
  49.     /**
  50.      * @var array
  51.      */
  52.     protected $rowGroups__ = [];
  53.     /**
  54.      * @var array
  55.      */
  56.     protected $rowGroupsSingletons__ = [];
  57.     /**
  58.      * @var CinemaAreaCategoryRepository
  59.      */
  60.     private $cinemaAreaCategoryRepository;
  61.     /**
  62.      * @var TicketRepository
  63.      */
  64.     private $ticketRepository;
  65.     /**
  66.      * @var Security
  67.      */
  68.     private $security;
  69.     /**
  70.      * @var TranslatorInterface
  71.      */
  72.     private $translator;
  73.     /**
  74.      * @var OrderRepositoryInterface
  75.      */
  76.     private $orderRepository;
  77.     /**
  78.      * @var string
  79.      */
  80.     private $rootDir;
  81.     /**
  82.      * @var LoggerInterface
  83.      */
  84.     private $logger;
  85.     /**
  86.      * @var Restrictions
  87.      */
  88.     private $restrictions;
  89.     /** @var SessionRepository */
  90.     private $sessionRepository;
  91.     /** @var SeatPlanLocalRepository */
  92.     protected $seatPlanLocalRepository;
  93.     /** @var SerializerInterface $serializer */
  94.     protected $serializer;
  95.     /** @var DenormalizerInterface */
  96.     protected $normalizer;
  97.     public function __construct(
  98.         SeatPlanRepository $seatPlanRepository,
  99.         OrderRepositoryInterface $orderRepository,
  100.         SessionRepository $sessionRepository,
  101.         CinemaAreaCategoryRepository $cinemaAreaCategoryRepository,
  102.         TicketRepository $ticketRepository,
  103.         Security $security,
  104.         TranslatorInterface $translator,
  105.         string $rootDir,
  106.         LoggerInterface $logger,
  107.         RestrictionsRepository $restrictionsRepository,
  108.         SeatPlanLocalRepository $seatPlanLocalRepository,
  109.         DenormalizerInterface $normalizer,
  110.         SerializerInterface $serializer
  111.     ) {
  112.         $this->seatPlanRepository $seatPlanRepository;
  113.         $this->cinemaAreaCategoryRepository $cinemaAreaCategoryRepository;
  114.         $this->orderRepository $orderRepository;
  115.         $this->ticketRepository $ticketRepository;
  116.         $this->security $security;
  117.         $this->translator $translator;
  118.         $this->rootDir $rootDir;
  119.         $this->logger $logger;
  120.         $this->restrictions $restrictionsRepository->findOrCreate();
  121.         $this->sessionRepository $sessionRepository;
  122.         $this->seatPlanLocalRepository $seatPlanLocalRepository;
  123.         $this->serializer $serializer;
  124.         $this->normalizer $normalizer;
  125.     }
  126.     /**
  127.      * @param object &$seatPlanTree
  128.      * @param string $cinemaId
  129.      *
  130.      */
  131.     public function setSeatPlanIcons(object &$seatPlanTreestring $cinemaId)
  132.     {
  133.         $categories $this->cinemaAreaCategoryRepository->find($cinemaId);
  134.         $icons $this->cinemaAreaCategoryRepository->getIcons();
  135.         foreach ($seatPlanTree->SeatLayoutData->Areas as &$arrea) {
  136.             $arrea->SeatsSpace 0;
  137.             $categoryId null;
  138.             if ($category $categories[intval($arrea->AreaCategoryCode)] ?? []) {
  139.                 $categoryId $category $category->getAreaCategoryId() : -1;
  140.             }
  141.             if ($columnIndex call_user_func(function ($rows) {
  142.                 $rowFirst null;
  143.                 foreach ($rows as $row) {
  144.                     if ($seats $row->getSeats()) {
  145.                         return $seats[0]->getPosition()->getColumnIndex();
  146.                     }
  147.                 }
  148.             }, $arrea->Rows)) {
  149.                 error_log(sprintf("%s %s(%s) columnIndex $columnIndex"date('Y-m-d H:i:s'), __METHOD____LINE__));
  150.             }
  151.             foreach ($arrea->Rows as &$row) {
  152.                 foreach ($row->Seats as &$seat) {
  153.                     $seat->iconUrl "";
  154.                     $seat->iconId "";
  155.                     if ($categoryId) {
  156.                         $seatObj = (new Seat())
  157.                             ->setAreaCategoryCode($arrea->AreaCategoryCode)
  158.                             ->setSeatStyle($seat->SeatStyle)
  159.                             ->setStatus($seat->Status)
  160.                             ->setOriginalStatus($seat->OriginalStatus);
  161.                         $seatsInGroup = [];
  162.                         if (is_array($seat->SeatsInGroup)) {
  163.                             foreach ($seat->SeatsInGroup as $group) {
  164.                                 $seatsInGroup[] = (array) $group;
  165.                             }
  166.                             //error_log(sprintf("%s %s(%s) ", date('Y-m-d H:i:s'), __METHOD__, __LINE__) . ' seat->SeatsInGroup ' . var_export($seat->SeatsInGroup, true));
  167.                         }
  168.                         $seatObj->setSeatsInGroup($seatsInGroup);
  169.                         $seat->iconId $this->seatPlanRepository->matcher->getIconId($seatObj$categoryId);
  170.                         $seat->improvedIconId $this->seatPlanRepository->matcher->getImprovedIconId($seatObj$categoryId);
  171.                         if (array_key_exists($seat->iconId$icons)) {
  172.                             $seat->iconUrl $icons[$seat->iconId]['imageUrl'];
  173.                         }
  174.                     }
  175.                     $position $seat->getPosition();
  176.                     //if (!is_int($position->getColumnIndexVista())) {
  177.                     //error_log(sprintf("%s %s(%s) ", date('Y-m-d H:i:s'), __METHOD__, __LINE__) . $position->getColumnIndex() . ' ' . $position->getColumnIndexVista());
  178.                     //$position->setColumnIndexVista($position->getColumnIndex());
  179.                     //$position->setColumnIndexRender($position->getColumnIndex());
  180.                     //}
  181.                 }
  182.             }
  183.         }
  184.     }
  185.     /**
  186.      * @param string $cinemaId
  187.      * @param string|null $sessionId
  188.      * @param string|null $responseJson
  189.      *
  190.      * @return SeatPlanResponse
  191.      */
  192.     public function getSeatPlan(string $cinemaIdstring $sessionIdstring $responseJson null$applySaved true): SeatPlanResponse
  193.     {
  194.         $start microtime(true);
  195.         $categories $this->cinemaAreaCategoryRepository->find($cinemaId);
  196.         $this->logger->info(sprintf("profiling seat selection :: cinemaAreaCategoryRepository->find execution time: %f"microtime(true) - $start));
  197.         $start microtime(true);
  198.         [$errorDescription$rows] = $this->seatPlanRepository->getSeatPlan($cinemaId$sessionId$categories);
  199.         $this->logger->info(sprintf("profiling seat selection :: seatPlanRepository->getSeatPlan execution time: %f"microtime(true) - $start));
  200.         if ($errorDescription) {
  201.             $this->logger->error('SeatPlanManager::getSeatPlan() Vista error response: ' $errorDescription);
  202.         }
  203.         $seatPlanSaved '0';
  204.         $icons = [];
  205.         $start microtime(true);
  206.         $rows $this->applySaved($rows$icons$cinemaId$sessionId$applySaved);
  207.         $this->logger->info(sprintf("profiling seat selection :: applySaved execution time: %f"microtime(true) - $start));
  208.         if ($seatPlanSaved === '0') {
  209.             $icons $this->cinemaAreaCategoryRepository->getIcons();
  210.         }
  211.         return (new SeatPlanResponse())
  212.             ->setRows($rows)
  213.             ->setIcons($icons)
  214.             ->setRowsMax($this->getMaxRows($rows))
  215.             ->setError($errorDescription)
  216.             ->setSeatPlanSaved($seatPlanSaved);
  217.     }
  218.     private function applySaved($rows, &$icons$cinemaId$sessionId$applySaved)
  219.     {
  220.         $session $this->sessionRepository->findOneBy(['sessionId' => $sessionId'cinemaId' => $cinemaId]);
  221.         $screenNumber $session->getScreenNumber();
  222.         if ($applySaved && $seatPlanJson $this->seatPlanLocalRepository->findOneBy(['cinemaId' => $cinemaId'screenNumber' => $screenNumber])) {
  223.             $seatPlanInput $seatPlanJson->getSeatPlanJson();
  224.             $responseJsonObj json_decode($seatPlanInput);
  225.             $rowsMax $responseJsonObj->rowsMax;
  226.             foreach ($responseJsonObj->icons as $icon) {
  227.                 $icons[] = $this->normalizer->denormalize($iconIcon::class);
  228.             }
  229.             $seatPlanSaved '1';
  230.             $rowsSaved = [];
  231.             foreach ($responseJsonObj->rows as $rowJson) {
  232.                 $row $this->serializer->deserialize(json_encode($rowJson), Row::class, 'json');
  233.                 $rowsSaved[] = $row;
  234.             }
  235.             //error_log(sprintf("%s %s(%s) screenNumber $screenNumber cinemaId $cinemaId ", date('Y-m-d H:i:s'),  __METHOD__, __LINE__) . ' count($rows) ' . count($rows) . ' count($rowsSaved) ' . count($rowsSaved));
  236.             error_log(sprintf("%s %s(%s) screenNumber $screenNumber cinemaId $cinemaId "date('Y-m-d H:i:s'),  __METHOD____LINE__) . ' count($rows) ' count($rows) . ' count($rowsSaved) ' count($rowsSaved));
  237.             $rows $this->reservationsToSaved2($rows$rowsSaved$rowsMax);
  238.             error_log(sprintf("%s %s(%s) screenNumber $screenNumber cinemaId $cinemaId "date('Y-m-d H:i:s'),  __METHOD____LINE__) . ' count($rows) ' count($rows) . ' count($rowsSaved) ' count($rowsSaved));
  239.         } else {
  240.             $this->setVistaColumns($rows);
  241.         }
  242.         return $rows;
  243.     }
  244.     public function getSavedRows($sessionId$cinemaId$screenNumber null)
  245.     {
  246.         if ($screenNumber == null && ($session $this->sessionRepository->findOneBy(['sessionId' => $sessionId'cinemaId' => $cinemaId]))) {
  247.             $screenNumber $session->getScreenNumber();
  248.         }
  249.         if ($screenNumber && ($seatPlanJson $this->seatPlanLocalRepository->findOneBy(['cinemaId' => $cinemaId'screenNumber' => $screenNumber]))) {
  250.             $seatPlanInput $seatPlanJson->getSeatPlanJson();
  251.             $responseJsonObj json_decode($seatPlanInput);
  252.             $rowsSaved = [];
  253.             foreach ($responseJsonObj->rows as $rowJson) {
  254.                 $row $this->serializer->deserialize(json_encode($rowJson), Row::class, 'json');
  255.                 $rowsSaved[] = $row;
  256.             }
  257.             return $rowsSaved;
  258.         }
  259.     }
  260.     public function setVistaColumns(&$rows)
  261.     {
  262.         if ($columnIndex call_user_func(function ($rows) {
  263.             $rowFirst null;
  264.             foreach ($rows as $row) {
  265.                 if ($seats $row->getSeats()) {
  266.                     return $seats[0]->getPosition()->getColumnIndex();
  267.                 }
  268.             }
  269.         }, $rows)) {
  270.             error_log(sprintf("%s %s(%s) columnIndex $columnIndex"date('Y-m-d H:i:s'), __METHOD____LINE__));
  271.         }
  272.         foreach ($rows as &$row) {
  273.             foreach ($row->getSeats() as &$seat) {
  274.                 $position $seat->getPosition();
  275.                 if (!is_int($position->getColumnIndexVista())) {
  276.                     $position->setColumnIndexVista($position->getColumnIndex());
  277.                 }
  278.                 if ($position->getColumnIndexRender() === null) {
  279.                     $position->setColumnIndexRender($position->getColumnIndex());
  280.                 }
  281.             }
  282.         }
  283.     }
  284.     /**
  285.      * @param Row[] $seatPlanRows
  286.      * @param $cinemaId
  287.      */
  288.     public function fillSeatPlanWithIcons($seatPlanRows$cinemaId)
  289.     {
  290.         $categories $this->cinemaAreaCategoryRepository->find($cinemaId);
  291.         $this->seatPlanRepository->setIconIds($seatPlanRows$categories);
  292.     }
  293.     /**
  294.      * @param Row[] $rows
  295.      *
  296.      * @return int
  297.      */
  298.     private function getMaxRows($rows): int
  299.     {
  300.         $max 0;
  301.         foreach ($rows as $row) {
  302.             if ($row && ($columnCount $row->getColumnCount()) > $max) {
  303.                 $max $columnCount;
  304.             }
  305.         }
  306.         return  $max;
  307.     }
  308.     /**
  309.      * @param Row[]     $seatPlanRows
  310.      * @param TicketDTO $query
  311.      * @param Order     $order
  312.      *
  313.      * @return array
  314.      */
  315.     public function getSeats(array $seatPlanRowsTicketDTO $queryOrder $order)
  316.     {
  317.         $this->logger->info(" ---> entering getSeats <---");
  318.         $selectedSeatData $query->getSeats()[0] ?? [];
  319.         $selectedSeat null;
  320.         $selectedCount 0;
  321.         if ($selectedSeatData) {
  322.             $this->logger->info(" ---> selectedSeatData available <---");
  323.             $selectedSeat = (new Seat())->setPosition($selectedSeatData);
  324.             $selectedCount 0
  325.                 + ($query->getNumberOfSeats() ? $query->getNumberOfSeats() : 0)
  326.                 + ($query->getNumberOfChildSeats() ? $query->getNumberOfChildSeats() : 0)
  327.                 + ($query->getNumberOfReducedSeats() ? $query->getNumberOfReducedSeats() : 0)
  328.                 + ($query->getNumberOfFilmBrunchSeats() ? $query->getNumberOfFilmBrunchSeats() : 0)
  329.                 + ($query->getNumberOfPackageReducedSeats() ? $query->getNumberOfPackageReducedSeats() : 0)
  330.                 + ($query->getNumberOfPackageRegularSeats() ? $query->getNumberOfPackageRegularSeats() : 0)
  331.                 + ($query->getNumberOfPackageClubSeats() ? $query->getNumberOfPackageClubSeats() : 0)
  332.                 + ($query->getNumberOfPackageChildSeats() ? $query->getNumberOfPackageChildSeats() : 0)
  333.                 + ($query->getNumberOfPackageWheelchairSeats() ? $query->getNumberOfPackageWheelchairSeats() : 0)
  334.                 + ($query->getNumberOfFilmBrunchChildSeats() ? $query->getNumberOfFilmBrunchChildSeats() : 0);
  335.         }
  336.         $selection $this->select(
  337.             $seatPlanRows,
  338.             $selectedSeat,
  339.             $selectedCount,
  340.             $order->getSelection()
  341.         );
  342.         /** @var SeatPosition[] $positions */
  343.         //$positions = array_map(function (Seat $item) { return $item->getPosition(); }, $selection);
  344.         $positions array_map(function (Seat &$seat) {
  345.             return $seat->getPosition();
  346.         }, $selection);
  347.         return [
  348.             $positions,
  349.             $selection,
  350.             $selectedSeatData,
  351.         ];
  352.     }
  353.     public function setTickets(Order $order, ?Session $sessionTicketDTO $query): SetTicketsOnServerResponse
  354.     {
  355.         // read drive in cinema mapping config
  356.         $di_mapping Yaml::parseFile($this->rootDir '/config/drive_in_mapping.yml');
  357.         $di_map null;
  358.         foreach ($di_mapping as $map) {
  359.             if ($session->getCinemaId() === $map['drive_in_cinema_id']) {
  360.                 $di_map $map;
  361.                 break;
  362.             }
  363.         }
  364.         $initSelection $order->getSelection();
  365.         if ($di_map) {
  366.             $this->logger->debug('drive in session selected');
  367.             $setTicketsOnServerResponse = (new SetTicketsOnServerResponse())->setOrder($order);
  368.             /** @var TicketDTO $entity */
  369.             $entity $query;
  370.             /** @var LoyaltyMember $user */
  371.             $user $this->security->getUser();
  372.             $sessionTicketTypes $this->ticketRepository->findByCinemaIdSessionId(
  373.                 $di_map['host_cinema_id'],
  374.                 $session->getSessionId(),
  375.                 $user $user->getUserSessionId() : null
  376.             );
  377.             // Add Car Tickets with bonus card            
  378.             if ($CBCCarCount $entity->getNumberOfCBCCar()) {
  379.                 foreach ($sessionTicketTypes as $type) {
  380.                     if (strpos($type->getDescriptionAlt(), $di_map['cbc_car_ticket_description_contains']) !== false) {
  381.                         break;
  382.                     }
  383.                 }
  384.                 $ticket = (new Ticket())->setTicketDetails((new TicketDetails())->setTicketTypeCode($type->getTicketTypeCode()));
  385.                 for ($i 0$i $CBCCarCount$i++) {
  386.                     $entity->addTicket($ticket);
  387.                 }
  388.             }
  389.             // Regular Car Ticket
  390.             if ($CarCount $entity->getNumberOfCar()) {
  391.                 foreach ($sessionTicketTypes as $type) {
  392.                     if (strpos($type->getDescriptionAlt(), $di_map['car_ticket_description_contains']) !== false) {
  393.                         break;
  394.                     }
  395.                 }
  396.                 $ticket = (new Ticket())->setTicketDetails((new TicketDetails())->setTicketTypeCode($type->getTicketTypeCode()));
  397.                 for ($i 0$i $CarCount$i++) {
  398.                     $entity->addTicket($ticket);
  399.                 }
  400.             }
  401.             // Car-Additional Ticket
  402.             if ($CarAdditionalCount $entity->getNumberOfAdditionalCar()) {
  403.                 foreach ($sessionTicketTypes as $type) {
  404.                     if (strpos($type->getDescriptionAlt(), $di_map['car_additional_ticket_description_contains']) !== false) {
  405.                         break;
  406.                     }
  407.                 }
  408.                 $ticket = (new Ticket())->setTicketDetails((new TicketDetails())->setTicketTypeCode($type->getTicketTypeCode()));
  409.                 for ($i 0$i $CarAdditionalCount$i++) {
  410.                     $entity->addTicket($ticket);
  411.                 }
  412.             }
  413.             // Car-Additional Ticket with bonus card
  414.             if ($CBCCarAdditionalCount $entity->getNumberOfCBCAdditionalCar()) {
  415.                 foreach ($sessionTicketTypes as $type) {
  416.                     if (strpos($type->getDescriptionAlt(), $di_map['cbc_car_additional_ticket_description_contains']) !== false) {
  417.                         break;
  418.                     }
  419.                 }
  420.                 $ticket = (new Ticket())->setTicketDetails((new TicketDetails())->setTicketTypeCode($type->getTicketTypeCode()));
  421.                 for ($i 0$i $CBCCarAdditionalCount$i++) {
  422.                     $entity->addTicket($ticket);
  423.                 }
  424.             }
  425.             // Car-Berth (Kojen) Ticket
  426.             if ($CarBerthCount $entity->getNumberOfBerthCar()) {
  427.                 foreach ($sessionTicketTypes as $type) {
  428.                     if (strpos($type->getDescriptionAlt(), $di_map['car_berth_ticket_description_contains']) !== false) {
  429.                         break;
  430.                     }
  431.                 }
  432.                 $ticket = (new Ticket())->setTicketDetails((new TicketDetails())->setTicketTypeCode($type->getTicketTypeCode()));
  433.                 for ($i 0$i $CarBerthCount$i++) {
  434.                     $entity->addTicket($ticket);
  435.                 }
  436.             }
  437.             // Car-Berth (Kojen) Ticket with bonus card
  438.             if ($CBCCarBerthCount $entity->getNumberOfCBCBerthCar()) {
  439.                 foreach ($sessionTicketTypes as $type) {
  440.                     if (strpos($type->getDescriptionAlt(), $di_map['cbc_car_berth_ticket_description_contains']) !== false) {
  441.                         break;
  442.                     }
  443.                 }
  444.                 $ticket = (new Ticket())->setTicketDetails((new TicketDetails())->setTicketTypeCode($type->getTicketTypeCode()));
  445.                 for ($i 0$i $CBCCarBerthCount$i++) {
  446.                     $entity->addTicket($ticket);
  447.                 }
  448.             }
  449.             $order $this->orderRepository->setTickets($order, (new SetTicketsForSessionRequest())->setTickets($entity->getTickets()), $session->getSessionId());
  450.             $this->orderRepository->save($order);
  451.             return $setTicketsOnServerResponse;
  452.         } else {
  453.             $this->logger->debug('standard (not drive in) session selected');
  454.             do {
  455.                 $seatPlanResponse null;
  456.                 try{
  457.                     $start microtime(true);
  458.                     $seatPlanResponse $this->getSeatPlan($order->getCinemaId(), $session->getSessionId(), nullfalse);
  459.                     $this->logger->info(sprintf("profiling seat selection :: getSeatPlan execution time: %f"microtime(true) - $start));
  460.                 } catch (SeatPlanException $spe){
  461.                     throw new SeatPlanException($this->translator->trans($spe->getMessage()));
  462.                 }
  463.                 $setTicketsOnServerResponse = (new SetTicketsOnServerResponse())
  464.                     ->setSeatPlan($seatPlanResponse)
  465.                     ->setOrder($order);
  466.                 $seatPlanRows $seatPlanResponse->getRows();
  467.                 if ($colunIndex call_user_func(function ($rows) {
  468.                     foreach ($rows as $row) {
  469.                         if ($seats $row->getSeats()) {
  470.                             return $seats[0]->getPosition()->getColumnIndex();
  471.                         }
  472.                     }
  473.                 }, $seatPlanRows)) {
  474.                     $this->logger->debug("columnIndex $colunIndex");
  475.                 }
  476.                 foreach ($seatPlanRows as &$seatPlanRow) {
  477.                     foreach ($seatPlanRow->getSeats() as &$seat) {
  478.                         $seat->getPosition()->setColumnIndex($seat->getPosition()->getColumnIndexVista());
  479.                     }
  480.                 }
  481.                 $start microtime(true);
  482.                 [$positions$selection$selectedSeatData] = $this->getSeats($seatPlanRows$query$order);
  483.                 $this->logger->info(sprintf("profiling seat selection :: this->getSeats execution time: %f"microtime(true) - $start));
  484.                 // without selection
  485.                 foreach ($positions as $position) {
  486.                     $position->setColumnIndex($position->getColumnIndexVista());
  487.                 }
  488.                 try {
  489.                     $this->checkGettingSeats($selection$selectedSeatData);
  490.                     /** @var TicketDTO $entity */
  491.                     $entity $query;
  492.                     /** @var LoyaltyMember $user */
  493.                     $user $this->security->getUser();
  494.                     $sessionTicketTypes $this->ticketRepository->findByCinemaIdSessionId(
  495.                         $session->getCinemaId(),
  496.                         $session->getSessionId(),
  497.                         $user $user->getUserSessionId() : null
  498.                     );
  499.                     
  500.                     $tickettypes '';
  501.                     foreach ($sessionTicketTypes as $tickettype) {
  502.                         $tickettypes .= sprintf('-> %s (Alt: %s)      '$tickettype->getDescription(), $tickettype->getDescriptionAlt());
  503.                     }
  504.                     $this->logger->debug(sprintf('available tickets [%s] [%s]: %s'$session->getCinemaId(), $session->getSessionId(), $tickettypes));
  505.                     
  506.                     [$normalized] = $this->getNormalized($seatPlanRows);
  507.                     $seats $this->getSeatsFromSeatPlan($normalized$positions);
  508.                     $wheelChairs from($seats)->where(function (array $item) {
  509.                         /** @var Seat $seat */
  510.                         [, $seat] = $item;
  511.                         return strpos($seat->getId(), 'R') !== false;
  512.                     })->toArray();
  513.                     $wheelChairsCount count($wheelChairs ?: []);
  514.                     if ($wheelChairsCount && in_array(getenv('APP_COUNTRY'), ['hrv''mne''srb'])) {
  515.                         $this->logger->debug('Disable wheelchair ticket booking');
  516.                         throw new SeatPlanException(
  517.                             $this->translator->trans(
  518.                                 'seat.no_wheelchair_tickets'
  519.                             )
  520.                         );
  521.                     }
  522.                     $filmBrunchAdultsCount $entity->getNumberOfFilmBrunchSeats();
  523.                     $filmBrunchChildrenCount $entity->getNumberOfFilmBrunchChildSeats();
  524.                     $packageWheelchairCount 0;
  525.                     $adultsCount $entity->getNumberOfSeats();
  526.                     $childrenCount $entity->getNumberOfChildSeats();
  527.                     $reducedCount $entity->getNumberOfReducedSeats();
  528.                     $packageReducedCount $entity->getNumberOfPackageReducedSeats();
  529.                     $packageRegularCount $entity->getNumberOfPackageRegularSeats();
  530.                     $packageClubCount $entity->getNumberOfPackageClubSeats();
  531.                     $packageChildCount $entity->getNumberOfPackageChildSeats();
  532.                     $packageWheelchairCount $entity->getNumberOfPackageWheelchairSeats();
  533.                     // required to be backwards compatibe. old app does not send packages count
  534.                     if ($packageReducedCount === null$packageReducedCount 0;
  535.                     if ($packageRegularCount === null$packageRegularCount 0;
  536.                     if ($packageClubCount === null$packageClubCount 0;
  537.                     if ($packageChildCount === null$packageChildCount 0;
  538.                     if ($packageWheelchairCount === null$packageWheelchairCount 0;
  539.                     if (($wheelChairsCount - ($filmBrunchAdultsCount $filmBrunchChildrenCount)) > 0) {
  540.                         $adultsCount -= ($wheelChairsCount - ($filmBrunchAdultsCount $filmBrunchChildrenCount));
  541.                     }
  542.                     ($adultsCount 0) and $adultsCount 0;
  543.                     $this->logger->debug(sprintf(
  544.                         '--> wheelchairs: %d    --> film brunch adult: %d    --> film brunch child: %d    --> adult: %d    --> children: %d    --> reduced: %d    --> packageRegular: %d    --> packageClub: %d    --> packageChild: %d    --> packageWheelchair: %d    --> packageReducedCount: %d',
  545.                         $wheelChairsCount,
  546.                         $filmBrunchAdultsCount,
  547.                         $filmBrunchChildrenCount,
  548.                         $adultsCount,
  549.                         $childrenCount,
  550.                         $reducedCount,
  551.                         $packageRegularCount,
  552.                         $packageClubCount,
  553.                         $packageChildCount,
  554.                         $packageWheelchairCount,
  555.                         $packageReducedCount
  556.                     ));
  557.                     $wheelChairsOrderSeats array_column($wheelChairs ?: [], 0);
  558.                     $notWheelChairs array_values(from($seats)->where(function (array $item) use ($wheelChairsOrderSeats) {
  559.                         [$orderSeat] = $item;
  560.                         return !array_filter($wheelChairsOrderSeats, function ($x) use ($orderSeat) {
  561.                             return $orderSeat === $x;
  562.                         });
  563.                     })->toArray() ?: []);
  564.                     $filmBrunchChildWheelChairsCount 0;
  565.                     $filmBrunchAdultWheelChairsCount 0;
  566.                     // convert child filmbrunchtickets to wheelchair child filmbrunch tickets
  567.                     $standardWheelChairsCount $wheelChairsCount $filmBrunchChildrenCount;
  568.                     if ($standardWheelChairsCount 1) {
  569.                         $standardWheelChairsCount 0;
  570.                         $filmBrunchChildWheelChairsCount $wheelChairsCount;
  571.                         $filmBrunchChildrenCount -= $wheelChairsCount;
  572.                         $wheelChairsCount 0;
  573.                     } else {
  574.                         $filmBrunchChildWheelChairsCount $filmBrunchChildrenCount;
  575.                         $filmBrunchChildrenCount 0;
  576.                         $wheelChairsCount -= $filmBrunchChildWheelChairsCount;
  577.                     }
  578.                     // convert the remaining wheelchair tickets to adult filmbrunch wheelchair tickets
  579.                     $standardWheelChairsCount $wheelChairsCount $filmBrunchAdultsCount;
  580.                     if ($standardWheelChairsCount 1) {
  581.                         $standardWheelChairsCount 0;
  582.                         $filmBrunchAdultWheelChairsCount $wheelChairsCount;
  583.                         $wheelChairsCount 0;
  584.                     } else {
  585.                         $filmBrunchAdultWheelChairsCount $filmBrunchAdultsCount;
  586.                         $filmBrunchAdultsCount 0;
  587.                         $wheelChairsCount -= $filmBrunchAdultWheelChairsCount;
  588.                     }
  589.                     $standardWheelChairs array_slice($wheelChairs0$standardWheelChairsCount);
  590.                     $filmBrunchAdultWheelChair array_slice($wheelChairs$standardWheelChairsCount$filmBrunchAdultWheelChairsCount);
  591.                     $filmBrunchChildWheelChair array_slice($wheelChairs$filmBrunchAdultWheelChairsCount $standardWheelChairsCount);
  592.                     $filmBrunchAdultsCount -= count($filmBrunchAdultWheelChair);
  593.                     $filmBrunchChildrenCount -= count($filmBrunchChildWheelChair);
  594.                     $this->logger->debug(sprintf(
  595.                         '--> wheelchairs: %d    --> film brunch adult: %d    --> film brunch child: %d    --> adult: %d    --> children: %d    --> reduced: %d    --> packageRegular: %d    --> packageClub: %d    --> packageChild: %d    --> packageWheelchair: %d     --> packageReducedCount: %d ',
  596.                         $standardWheelChairsCount,
  597.                         $filmBrunchAdultsCount,
  598.                         $filmBrunchChildrenCount,
  599.                         $adultsCount,
  600.                         $childrenCount,
  601.                         $reducedCount,
  602.                         $packageRegularCount,
  603.                         $packageClubCount,
  604.                         $packageChildCount,
  605.                         $packageWheelchairCount,
  606.                         $packageReducedCount
  607.                     ));
  608.                     $adults array_slice($notWheelChairs0$adultsCount);
  609.                     $children array_slice($notWheelChairs$adultsCount$childrenCount);
  610.                     $reduced array_slice($notWheelChairs$adultsCount $childrenCount$reducedCount);
  611.                     $filmBrunchAdults array_slice($notWheelChairs$adultsCount $childrenCount $reducedCount$filmBrunchAdultsCount);
  612.                     $filmBrunchChildren array_slice($notWheelChairs$adultsCount $childrenCount $filmBrunchAdultsCount $reducedCount$filmBrunchChildrenCount);
  613.                     $packageReducedSeats array_slice($notWheelChairs$adultsCount $childrenCount$packageReducedCount);
  614.                     $packageRegularSeats array_slice($notWheelChairs$adultsCount $childrenCount$packageRegularCount);
  615.                     $packageClubSeats array_slice($notWheelChairs$adultsCount $childrenCount$packageClubCount);
  616.                     $packageChildSeats array_slice($notWheelChairs$adultsCount $childrenCount$packageChildCount);
  617.                     $packageWheelchairSeats array_slice($notWheelChairs$adultsCount $childrenCount$packageWheelchairCount);
  618.                     $this->logger->debug(sprintf(
  619.                         'Array Count: wheelchair: %d   not wheelchair: %d   fbwheelchair a: %d   fbwheelchair c: %d   adult: %d   child: %d  reduced: %d   fb a: %d   fb c: %d   packageReducedSeats: %d   packageRegularSeats: %d   packageClubSeats: %d   packageChildSeats: %d   packageWheelchairSeats: %d',
  620.                         count($standardWheelChairs),
  621.                         count($notWheelChairs),
  622.                         count($filmBrunchAdultWheelChair),
  623.                         count($filmBrunchChildWheelChair),
  624.                         count($adults),
  625.                         count($children),
  626.                         count($reduced),
  627.                         count($filmBrunchAdults),
  628.                         count($filmBrunchChildren),
  629.                         count($packageReducedSeats),
  630.                         count($packageRegularSeats),
  631.                         count($packageClubSeats),
  632.                         count($packageChildSeats),
  633.                         count($packageWheelchairSeats)
  634.                     ));
  635.                     $sources = [
  636.                         [
  637.                             array_column($standardWheelChairs0),
  638.                             function ($seat) use ($sessionTicketTypes$seatPlanRows$entity) {
  639.                                 return $this->findTicket($sessionTicketTypes$seatPlanRows$seat$entity->isBonusCard(), falsefalsefalse);
  640.                             }
  641.                         ],
  642.                         [
  643.                             array_column($filmBrunchAdultWheelChair0),
  644.                             function ($seat) use ($sessionTicketTypes$seatPlanRows$entity) {
  645.                                 return $this->findTicket($sessionTicketTypes$seatPlanRows$seat$entity->isBonusCard(), falsefalsetrue);
  646.                             }
  647.                         ],
  648.                         [
  649.                             array_column($filmBrunchChildWheelChair0),
  650.                             function ($seat) use ($sessionTicketTypes$seatPlanRows$entity) {
  651.                                 return $this->findTicket($sessionTicketTypes$seatPlanRows$seat$entity->isBonusCard(), truefalsetrue);
  652.                             }
  653.                         ],
  654.                         [
  655.                             array_column($adults0),
  656.                             function ($seat) use ($sessionTicketTypes$seatPlanRows$entity) {
  657.                                 return $this->findTicket($sessionTicketTypes$seatPlanRows$seat$entity->isBonusCard(), falsefalsefalse);
  658.                             }
  659.                         ],
  660.                         [
  661.                             array_column($children0),
  662.                             function ($seat) use ($sessionTicketTypes$seatPlanRows) {
  663.                                 return $this->findTicket($sessionTicketTypes$seatPlanRows$seatfalsetruefalsefalse);
  664.                             }
  665.                         ],
  666.                         [
  667.                             array_column($reduced0),
  668.                             function ($seat) use ($sessionTicketTypes$seatPlanRows) {
  669.                                 return $this->findTicket($sessionTicketTypes$seatPlanRows$seatfalsefalsetruefalse);
  670.                             }
  671.                         ],
  672.                         [
  673.                             array_column($filmBrunchAdults0),
  674.                             function ($seat) use ($sessionTicketTypes$seatPlanRows) {
  675.                                 return $this->findTicket($sessionTicketTypes$seatPlanRows$seatfalsefalsefalsetrue);
  676.                             }
  677.                         ],
  678.                         [
  679.                             array_column($filmBrunchChildren0),
  680.                             function ($seat) use ($sessionTicketTypes$seatPlanRows) {
  681.                                 return $this->findTicket($sessionTicketTypes$seatPlanRows$seatfalsetruefalsetrue);
  682.                             }
  683.                         ],
  684.                         [
  685.                             array_column($packageReducedSeats0),
  686.                             function ($seat) use ($sessionTicketTypes$seatPlanRows) {
  687.                                 return $this->findTicket($sessionTicketTypes$seatPlanRows$seatfalsefalsefalsefalsefalsetrue);
  688.                             }
  689.                         ],
  690.                         [
  691.                             array_column($packageRegularSeats0),
  692.                             function ($seat) use ($sessionTicketTypes$seatPlanRows) {
  693.                                 return $this->findTicket($sessionTicketTypes$seatPlanRows$seatfalsefalsefalsefalsetrue);
  694.                             }
  695.                         ],
  696.                         [
  697.                             array_column($packageClubSeats0),
  698.                             function ($seat) use ($sessionTicketTypes$seatPlanRows$entity) {
  699.                                 return $this->findTicket($sessionTicketTypes$seatPlanRows$seat$entity->isBonusCard(), falsefalsefalsefalsefalsetrue);
  700.                             }
  701.                         ],
  702.                         [
  703.                             array_column($packageChildSeats0),
  704.                             function ($seat) use ($sessionTicketTypes$seatPlanRows) {
  705.                                 return $this->findTicket($sessionTicketTypes$seatPlanRows$seatfalsefalsefalsefalsefalsefalsefalsetrue);
  706.                             }
  707.                         ],
  708.                         [
  709.                             array_column($packageWheelchairSeats0),
  710.                             function ($seat) use ($sessionTicketTypes$seatPlanRows) {
  711.                                 return $this->findTicket($sessionTicketTypes$seatPlanRows$seatfalsefalsefalsefalse);
  712.                             }
  713.                         ]
  714.                     ];
  715.                     foreach ($seats as $item) {
  716.                         list($targetOrderSeat) = $item;
  717.                         foreach ($sources as $s) {
  718.                             foreach ($s[0] as $orderSeat) {
  719.                                 if (
  720.                                     $targetOrderSeat->getRowIndex() == $orderSeat->getRowIndex() &&
  721.                                     $targetOrderSeat->getColumnIndex() == $orderSeat->getColumnIndex() &&
  722.                                     $targetOrderSeat->getAreaNumber() == $orderSeat->getAreaNumber()
  723.                                 ) {
  724.                                     $targetOrderSeat->setColumnIndexVista($targetOrderSeat->getColumnIndex());
  725.                                     $targetOrderSeat->setColumnIndex($targetOrderSeat->getColumnIndexVista());
  726.                                     $entity->addTicket($s[1]($targetOrderSeat));
  727.                                 }
  728.                             }
  729.                         }
  730.                     }
  731.                     $start microtime(true);
  732.                     $order $this->orderRepository->setTickets($order, (new SetTicketsForSessionRequest())->setTickets($entity->getTickets()), $session->getSessionId());
  733.                     $this->logger->info(sprintf("profiling seat selection :: orderRepository->setTickets execution time: %f"microtime(true) - $start));
  734.                     $order->setSelection($selection);
  735.                     $start microtime(true);
  736.                     $this->orderRepository->save($order);
  737.                     $this->logger->info(sprintf("profiling seat selection :: orderRepository->save execution time: %f"microtime(true) - $start));
  738.                     break;
  739.                 } catch (\Exception $ex) {
  740.                     $this->logger->error(sprintf("Exception in SeatPlanManager.php (%d): %s "__LINE__$ex->getMessage()));
  741.                     if ($this->shouldRetry($ex$setTicketsOnServerResponse)) {
  742.                         $this->logger->info('retrying');
  743.                         continue;
  744.                     }
  745.                 }
  746.             } while ($seatPlanRows && !$setTicketsOnServerResponse->getErrorMessage());
  747.         }
  748.         if ($seatPlanRows && $initSelection) {
  749.             $this->cleanUpSelection($seatPlanRows$initSelection);
  750.         }
  751.         $this->getNormalized($seatPlanRowsnull$order->getSelection());
  752.         $this->fillSeatPlanWithIcons($seatPlanRows$order->getCinemaId());
  753.         foreach ($seatPlanRows as &$row) {
  754.             foreach ($row->getSeats() as &$seat) {
  755.                 $seat->getPosition()->setColumnIndex($seat->getPosition()->getColumnIndexRender());
  756.             }
  757.         }
  758.         $icons = [];
  759.         $seatPlanRows $this->applySaved($seatPlanRows$icons$order->getCinemaId(), $session->getSessionId(), true);
  760.         $setTicketsOnServerResponse->getSeatPlan()->setRows($seatPlanRows);
  761.         // remove next rows after first one
  762.         // $preparedRows = [];
  763.         // $previousPhysicalName = null;
  764.         // foreach($setTicketsOnServerResponse->getSeatPlan()->getRows() as $row) {
  765.         // if ($row->getPhysicalName() != null && $previousPhysicalName == $row->getPhysicalName()) {
  766.         // continue;
  767.         // }
  768.         // $previousPhysicalName = $row->getPhysicalName();
  769.         // $preparedRows[] = $row;
  770.         // }
  771.         // $setTicketsOnServerResponse->getSeatPlan()->setRows($preparedRows);
  772.         return $setTicketsOnServerResponse;
  773.     }
  774.     /**
  775.      * @param array        $sessionTicketTypes
  776.      * @param array        $seatPlanRows
  777.      * @param SeatPosition $seat
  778.      * @param bool         $isBonus
  779.      * @param bool         $isChild
  780.      *
  781.      * @return Ticket
  782.      * @throws SeatPlanException
  783.      */
  784.     protected function findTicket(
  785.         array $sessionTicketTypes,
  786.         array $seatPlanRows,
  787.         SeatPosition $seat,
  788.         bool $isBonus,
  789.         bool $isChild false,
  790.         bool $isReduced false,
  791.         bool $isFilmBrunch false,
  792.         bool $isPackageRegular false,
  793.         bool $isPackageReduced false,
  794.         bool $isPackageClub false,
  795.         bool $isPackageChild false
  796.     ): Ticket {
  797.         //$this->logger->debug("entering findTicket() -- isBonus: [$isBonus] ");
  798.         $this->logger->debug("entering findTicket()");
  799.         [$areaCategoryCode$isWheelchair] = $this->findAreaCategoryCodeBySeat($seatPlanRows$seat);
  800.         foreach ($sessionTicketTypes as $item) {
  801.             /*
  802.             $packageConcessions = $item->getPackageContent()->getConcessions();
  803.             $packageTickets = $item->getPackageContent()->getTickets();*/
  804.             if (
  805.                 1
  806.                 && intval($item->getAreaCategoryCode()) === intval($areaCategoryCode)
  807.                 //                && ($packageConcessions === null || count($packageConcessions) === 0)
  808.                 //                && ($packageTickets === null || count($packageTickets) === 0)
  809.             ) {
  810.                 if (
  811.                     1
  812.                     && strpos($item->getDescriptionAlt(), 'Wheelchair') !== false
  813.                     && strpos($item->getDescriptionAlt(), 'Package Wheelchair') === false
  814.                     && strpos($item->getDescriptionAlt(), 'PCK') === false
  815.                     && !$item->isChildOnlyTicket()
  816.                     && $isWheelchair
  817.                     && !$isChild
  818.                     && !$isReduced
  819.                     && !$isFilmBrunch
  820.                 ) {
  821.                     $this->logger->info('selecting standard wheelchair ticket');
  822.                     return $this->getTicketDetails($item$seatfalse);
  823.                 } elseif (
  824.                     1
  825.                     && strpos($item->getDescriptionAlt(), 'Package Wheelchair') !== false
  826.                     && !$item->isChildOnlyTicket()
  827.                     && $isWheelchair
  828.                     && !$isChild
  829.                     && !$isReduced
  830.                     && !$isFilmBrunch
  831.                 ) {
  832.                     $this->logger->info('selecting Package Wheelchair ticket');
  833.                     return $this->getTicketDetails($item$seatfalse);
  834.                 } elseif (
  835.                     1
  836.                     && strpos($item->getDescriptionAlt(), 'Child') !== false
  837.                     && strpos($item->getDescriptionAlt(), 'Package Child') === false
  838.                     && strpos($item->getDescriptionAlt(), 'PCK') === false
  839.                     && $item->isChildOnlyTicket()
  840.                     && !$isWheelchair
  841.                     && $isChild
  842.                     && !$isReduced
  843.                     && !$isBonus
  844.                     && !$isFilmBrunch
  845.                 ) {
  846.                     $this->logger->info('selecting child ticket');
  847.                     return $this->getTicketDetails($item$seatfalse);
  848.                 } elseif (
  849.                     1
  850.                     && strpos($item->getDescriptionAlt(), 'Package Child') !== false
  851.                     && $item->isChildOnlyTicket()
  852.                     && !$isWheelchair
  853.                     && !$isChild
  854.                     && !$isReduced
  855.                     && !$isBonus
  856.                     && !$isFilmBrunch
  857.                     && $isPackageChild
  858.                 ) {
  859.                     $this->logger->info('selecting Package Child');
  860.                     return $this->getTicketDetails($item$seatfalse);
  861.                 } elseif (
  862.                     1
  863.                     && ( stripos($item->getDescriptionAlt(), 'Ermäßigt') !== false || stripos($item->getDescriptionAlt(), 'Student') !== false )
  864.                     && !$item->isChildOnlyTicket()
  865.                     && !$isWheelchair
  866.                     && !$isChild
  867.                     && $isReduced
  868.                     && !$isBonus
  869.                     && !$isFilmBrunch
  870.                 ) {
  871.                     $this->logger->info('selecting reduced (Ermäßigt) ticket');
  872.                     return $this->getTicketDetails($item$seatfalse);
  873.                 } elseif (
  874.                     1
  875.                     && strpos($item->getDescriptionAlt(), 'Filmbrunch Pkg RE') !== false
  876.                     && $isWheelchair
  877.                     && !$isChild
  878.                     && !$isReduced
  879.                     && $isFilmBrunch
  880.                 ) {
  881.                     $this->logger->info('selecting filmbrunch adult wheelchair ticket');
  882.                     return $this->getTicketDetails($item$seatfalse);
  883.                 } elseif (
  884.                     1
  885.                     && strpos($item->getDescriptionAlt(), 'Filmbrunch Pkg RK') !== false
  886.                     && $isWheelchair
  887.                     && $isChild
  888.                     && !$isReduced
  889.                     && $isFilmBrunch
  890.                 ) {
  891.                     $this->logger->info('selecting filmbrunch child wheelchair ticket');
  892.                     return $this->getTicketDetails($item$seatfalse);
  893.                 } elseif (
  894.                     1
  895.                     && strpos($item->getDescriptionAlt(), 'CBC') !== false
  896.                     && !$item->isChildOnlyTicket()
  897.                     && !$isWheelchair
  898.                     && !$isChild
  899.                     && !$isReduced
  900.                     && $isBonus
  901.                     && !$isFilmBrunch
  902.                 ) {
  903.                     $this->logger->info('selecting CBC ticket');
  904.                     return $this->getTicketDetails($item$seattrue);
  905.                 } elseif (
  906.                     1
  907.                     && strpos($item->getDescriptionAlt(), 'Package Club') !== false
  908.                     && !$item->isChildOnlyTicket()
  909.                     && !$isWheelchair
  910.                     && !$isChild
  911.                     && !$isReduced
  912.                     && $isBonus
  913.                     && !$isFilmBrunch
  914.                     && $isPackageClub
  915.                 ) {
  916.                     $this->logger->info('selecting Package Club');
  917.                     return $this->getTicketDetails($item$seatfalse);
  918.                 } elseif (
  919.                     1
  920.                     && strpos($item->getDescriptionAlt(), 'Regular') !== false
  921.                     && strpos($item->getDescriptionAlt(), 'Package Regular') === false
  922.                     && strpos($item->getDescriptionAlt(), 'PCK') === false
  923.                     && !$item->isChildOnlyTicket()
  924.                     && !$isWheelchair
  925.                     && !$isChild
  926.                     && !$isReduced
  927.                     && !$isBonus
  928.                     && !$isFilmBrunch
  929.                     && !$isPackageRegular
  930.                 ) {
  931.                     $this->logger->info('selecting adult ticket');
  932.                     return $this->getTicketDetails($item$seatfalse);
  933.                 } elseif (
  934.                     1
  935.                     && strpos($item->getDescriptionAlt(), 'Package Regular') !== false
  936.                     && !$item->isChildOnlyTicket()
  937.                     && !$isWheelchair
  938.                     && !$isChild
  939.                     && !$isReduced
  940.                     && !$isBonus
  941.                     && !$isFilmBrunch
  942.                     && !$isPackageReduced
  943.                     && !$isPackageClub
  944.                     && $isPackageRegular
  945.                 ) {
  946.                     $this->logger->info('selecting Package Regular');
  947.                     return $this->getTicketDetails($item$seatfalse);
  948.                 } elseif (
  949.                     1
  950.                     && strpos($item->getDescriptionAlt(), 'Package Reduced') !== false
  951.                     && !$item->isChildOnlyTicket()
  952.                     && !$isWheelchair
  953.                     && !$isChild
  954.                     && !$isReduced
  955.                     && !$isFilmBrunch
  956.                     && $isPackageReduced
  957.                 ) {
  958.                     $this->logger->info('selecting Package Reduced');
  959.                     return $this->getTicketDetails($item$seatfalse);
  960.                 } elseif (
  961.                     1
  962.                     && strpos($item->getDescriptionAlt(), 'Filmbrunch Pkg E') !== false
  963.                     && !$item->isChildOnlyTicket()
  964.                     && !$isWheelchair
  965.                     && !$isChild
  966.                     && !$isReduced
  967.                     && $isFilmBrunch
  968.                 ) {
  969.                     $this->logger->info('selecting filmbrunch adult ticket');
  970.                     return $this->getTicketDetails($item$seatfalse);
  971.                 } elseif (
  972.                     1
  973.                     && strpos($item->getDescriptionAlt(), 'Filmbrunch Pkg K') !== false
  974.                     && !$item->isChildOnlyTicket()
  975.                     && !$isWheelchair
  976.                     && $isChild
  977.                     && !$isReduced
  978.                     && $isFilmBrunch
  979.                 ) {
  980.                     $this->logger->info('selecting filmbrunch child ticket');
  981.                     return $this->getTicketDetails($item$seatfalse);
  982.                 } elseif (
  983.                     1
  984.                     && strpos($item->getDescriptionAlt(), 'Filmbrunch Pkg K') !== false
  985.                     && !$item->isChildOnlyTicket()
  986.                     && $isChild
  987.                     && !$isReduced
  988.                     && $isFilmBrunch
  989.                 ) {
  990.                     return $this->getTicketDetails($item$seatfalse);
  991.                 } elseif (
  992.                     1
  993.                     && strpos($item->getDescriptionAlt(), 'Filmbrunch Pkg K') !== false
  994.                     && !$item->isChildOnlyTicket()
  995.                     && $isChild
  996.                     && !$isReduced
  997.                     && $isFilmBrunch
  998.                 ) {
  999.                     error_log(sprintf("%s %s(%s) isChildOnlyTicket {$item->isChildOnlyTicket()} getTicketTypeCode {$item->getTicketTypeCode()} "date('Y-m-d H:i:s'), __METHOD____LINE__)); // . var_export([$item, $seat], true)
  1000.                     return $this->getTicketDetails($item$seatfalse);
  1001.                 }
  1002.             }/* else {
  1003.                 $this->logger->info(sprintf('%s is excluded', $item->getDescriptionAlt()));
  1004.             }*/
  1005.         }
  1006.         if ($isChild) {
  1007.             throw SeatPlanException::create(
  1008.                 'No child tickets available for the selection',
  1009.                 SeatPlanException::CODE_SEATPLAN_NO_CHILD_TICKET_IS_AVAILABLE
  1010.             );
  1011.         } else {
  1012.             throw new SeatPlanException(
  1013.                 $this->translator->trans(
  1014.                     'seat.no_tickets',
  1015.                     [
  1016.                         '%area%' => $seat->getAreaNumber(),
  1017.                         '%row%' => $seat->getRowIndex(),
  1018.                         '%column%' => $seat->getColumnIndex(),
  1019.                     ]
  1020.                 )
  1021.             );
  1022.         }
  1023.     }
  1024.     protected function findAreaCategoryCodeBySeat(array $rowsSeatPosition $orderSeat): array
  1025.     {
  1026.         $count = -1;
  1027.         //error_log(sprintf("%s %s(%s) getAreaNumber {$orderSeat->getAreaNumber()} getRowIndex {$orderSeat->getRowIndex()} getColumnIndex {$orderSeat->getColumnIndex()}", date('Y-m-d H:i:s'), __METHOD__, __LINE__));
  1028.         /** @var Row $row */
  1029.         foreach ($rows ?: [] as $row) {
  1030.             //error_log(sprintf("%s %s(%s) getPhysicalName {$row->getPhysicalName()} getAreaCategoryCode {$row->getAreaCategoryCode()} count %s ", 
  1031.             //  date('Y-m-d H:i:s'), __METHOD__, __LINE__, ++$count));
  1032.             /** @var Seat $seat */
  1033.             foreach ($row->getSeats() ?: [] as $seat) {
  1034.                 $position $seat->getPosition();
  1035.                 // if($row->getPhysicalName() == 13) {
  1036.                 // error_log(sprintf("%s %s(%s) getPhysicalName {$row->getPhysicalName()} getAreaNumber {$position->getAreaNumber()} getRowIndex {$position->getRowIndex()} getColumnIndex {$position->getColumnIndex()} count %s ", date('Y-m-d H:i:s'), __METHOD__, __LINE__, $count));
  1037.                 // }
  1038.                 if (
  1039.                     1
  1040.                     && ($position->getAreaNumber() === $orderSeat->getAreaNumber())
  1041.                     && ($position->getRowIndex() === $orderSeat->getRowIndex())
  1042.                     && ($position->getColumnIndex() === $orderSeat->getColumnIndex())
  1043.                 ) {
  1044.                     // error_log(sprintf("%s %s(%s) match!!! getPhysicalName {$row->getPhysicalName()} getAreaNumber {$position->getAreaNumber()} getRowIndex {$position->getRowIndex()} getColumnIndex {$position->getColumnIndex()} getAreaCategoryCode {$seat->getAreaCategoryCode()} count %s ", date('Y-m-d H:i:s'), __METHOD__, __LINE__, $count));
  1045.                     return [
  1046.                         $row->getAreaCategoryCode(),
  1047.                         strpos($seat->getId(), 'R') !== false
  1048.                     ];
  1049.                 }
  1050.             }
  1051.         }
  1052.         throw new \InvalidArgumentException(sprintf(
  1053.             'Invalid seat position - Area number: %s, Row index: %s, Column index: %s',
  1054.             $orderSeat->getAreaNumber(),
  1055.             $orderSeat->getRowIndex(),
  1056.             $orderSeat->getColumnIndex()
  1057.         ));
  1058.     }
  1059.     protected function getTicketDetails(SessionTicketType $ticketSeatPosition $seatbool $isBonus): Ticket
  1060.     {
  1061.         $ticketDetails = (new TicketDetails())->setTicketTypeCode($ticket->getTicketTypeCode());
  1062.         /** @var LoyaltyMember $user */
  1063.         if ($isBonus && ($user $this->security->getUser())) {
  1064.             $ticketDetails->setThirdPartyMemberScheme(
  1065.                 (new ThirdPartyMemberScheme())
  1066.                     ->setMemberDateOfBirth($user->getDateOfBirth())
  1067.                     ->setMemberCard($user->getCardNumber())
  1068.             );
  1069.         }
  1070.         $orderSeat = new OrderSeat();
  1071.         $orderSeat->setColumnIndex($seat->getColumnIndex())
  1072.             ->setAreaNumber($seat->getAreaNumber())
  1073.             ->setRowIndex($seat->getRowIndex());
  1074.         return (new Ticket())->setTicketDetails($ticketDetails)->setSeats([$orderSeat]);
  1075.     }
  1076.     protected function checkGettingSeats($selection$selectedSeatData)
  1077.     {
  1078.         $this->logger->info(" ---> entering checkGettingsSeats <---");
  1079.         if (!$selectedSeatData) {
  1080.             throw SeatPlanException::create(
  1081.                 'No seat is selected!',
  1082.                 SeatPlanException::CODE_SEATPLAN_NO_SEAT_IS_SELECTED
  1083.             );
  1084.         }
  1085.         $this->logger->info(" ---> entering checkGettingsSeats <---");
  1086.         if (!$selection) {
  1087.             throw SeatPlanException::create(
  1088.                 $this->translator->trans('No selection is possible with such a criteria'),
  1089.                 SeatPlanException::CODE_SEATPLAN_NOTHING_COULD_BE_SELECTED
  1090.             );
  1091.         }
  1092.     }
  1093.     /**
  1094.      * Check if error is recoverable
  1095.      *
  1096.      * @param \Exception                 $exception
  1097.      * @param SetTicketsOnServerResponse $response
  1098.      *
  1099.      * @return bool
  1100.      */
  1101.     private function shouldRetry(\Exception $exceptionSetTicketsOnServerResponse $response): bool
  1102.     {
  1103.         $this->logger->debug('message: '.$exception->getMessage());
  1104.         if (!$exception instanceof SeatPlanException) {
  1105.             if (!$response->getErrorMessage()) {
  1106.                 $response->setErrorMessage($this->translator->trans($exception->getMessage()));
  1107.             }
  1108.             $this->logger->error('not a seatplan exception, omitting retry');
  1109.             return false;
  1110.         }
  1111.         $response->setErrorMessage($exception->getMessage());
  1112.         
  1113.         if (!$data $exception->getData()) {
  1114.             return false;
  1115.         }
  1116.         // if(!array_key_exists('errorCode', $data)) {
  1117.         // error_log(sprintf("%s %s(%s) data ", date('Y-m-d H:i:s'), __METHOD__, __LINE__) . var_export([$data, debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3)], true));
  1118.         // }
  1119.         if ($data['errorCode'] === '110') {
  1120.             $response->setErrorMessage($this->translator->trans('Seat sold out'));
  1121.             return true;
  1122.         }
  1123.         if ($data['errorCode'] === '140') {
  1124.             $response->setErrorMessage($this->translator->trans('Limit of CBC Tickets for the day requested has been reached'));
  1125.             return false;
  1126.         }
  1127.         return false;
  1128.     }
  1129.     /**
  1130.      * @param Row[]   $seatPlanRows
  1131.      * @param Seat    $selectedSeat
  1132.      * @param int     $selectedCount
  1133.      * @param Seat[]  $initSelection
  1134.      *
  1135.      * @return array
  1136.      */
  1137.     public function select(array $seatPlanRows, ?Seat $selectedSeatint $selectedCount 0, array $initSelection = []): array
  1138.     {
  1139.         if (!$selectedSeat || !$selectedCount) {
  1140.             return [];
  1141.         }
  1142.         return $this->doSelect($seatPlanRows$selectedSeat$selectedCount$initSelection);
  1143.     }
  1144.     /**
  1145.      * @param Row[]  $seatPlan
  1146.      * @param Seat   $selectedSeat
  1147.      * @param int    $selectedCount
  1148.      * @param Seat[] $initSelection
  1149.      *
  1150.      * @return array
  1151.      */
  1152.     protected function doSelect(array $seatPlanSeat $selectedSeatint $selectedCount, array $initSelection = []): array
  1153.     {
  1154.         // error_log(sprintf("%s %s(%s) selectedSeat ", date('Y-m-d H:i:s'), __METHOD__, __LINE__) . var_export($selectedSeat, true));//,debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5)]
  1155.         list($normalized$selectedSeat) = $this->getNormalized(
  1156.             $seatPlan,
  1157.             $selectedSeat,
  1158.             $initSelection
  1159.         );
  1160.         //error_log(sprintf("%s %s(%s) normalized,selectedSeat ", date('Y-m-d H:i:s'), __METHOD__, __LINE__) . var_export([array_slice($normalized[9],0,4), $selectedSeat], true));
  1161.         $list $this->getWalkThroughIndexes(array_keys($normalized), $selectedSeat->getNormalizedRowIndex());
  1162.         foreach ($list as $index) {
  1163.             if ($selection $this->processRow($normalized[$index], $selectedSeat$selectedCount)) {
  1164.                 return $selection;
  1165.             }
  1166.         }
  1167.         $this->logger->debug('returning empty array');
  1168.         return [];
  1169.     }
  1170.     protected function processRow($rowSeat $selectedSeat$selectedCount)
  1171.     {
  1172.         $this->rowGroups__ $rowGroups $this->extractRanges(
  1173.             $row,
  1174.             $selectedSeat,
  1175.             function ($index$seat) {
  1176.                 /** @var Seat $seat */
  1177.                 return intval($seat->getColumnIndex());
  1178.             },
  1179.             1,
  1180.             false
  1181.         );
  1182.         $ranges $this->extractRanges(
  1183.             $row,
  1184.             $selectedSeat,
  1185.             function ($index$seat) {
  1186.                 /** @var Seat $seat */
  1187.                 return intval($index);
  1188.             },
  1189.             $selectedCount
  1190.         );
  1191.         $availableSelections = [];
  1192.         if ($this->restrictions->getAllowBookingSplitting()) {
  1193.             // use unfiltered rowGroups
  1194.             $this->logger->debug('splitted booking is allowed');
  1195.             foreach ($rowGroups as $item) {
  1196.                 $selection $this->processRange($row$item$selectedSeat$selectedCount);
  1197.                 $selection and $availableSelections[] = $selection;
  1198.             }
  1199.         } else {
  1200.             // use filtered ranges to avoid splitted ticket bookings
  1201.             $this->logger->debug('splitted booking is NOT allowed');
  1202.             foreach ($ranges as $item) {
  1203.                 $selection $this->processRange($row$item$selectedSeat$selectedCount);
  1204.                 $selection and $availableSelections[] = $selection;
  1205.             }
  1206.         }
  1207.         // error_log(sprintf("%s %s(%s) availableSelections ", date('Y-m-d H:i:s'), __METHOD__, __LINE__) . var_export(count($availableSelections), true));
  1208.         usort($availableSelections, function ($a$b) use ($selectedSeat) {
  1209.             $aDistance abs($a[0]->getNormalizedColumnIndex() - $selectedSeat->getNormalizedColumnIndex());
  1210.             $bDistance abs($b[0]->getNormalizedColumnIndex() - $selectedSeat->getNormalizedColumnIndex());
  1211.             if ($aDistance $bDistance) {
  1212.                 return -1;
  1213.             } elseif ($aDistance $bDistance) {
  1214.                 return 1;
  1215.             }
  1216.             return 0;
  1217.         });
  1218.         // error_log(sprintf("%s %s(%s) availableSelections ", date('Y-m-d H:i:s'), __METHOD__, __LINE__) . var_export(count($availableSelections), true));
  1219.         if ($availableSelections) {
  1220.             return array_shift($availableSelections);
  1221.         }
  1222.         return [];
  1223.     }
  1224.     /**
  1225.      * @param Seat[] $row
  1226.      * @param Seat[] $range
  1227.      * @param Seat   $selectedSeat
  1228.      * @param int    $selectedCount
  1229.      *
  1230.      * @return array
  1231.      */
  1232.     protected function processRange(array $row, array $rangeSeat $selectedSeatint $selectedCount): array
  1233.     {
  1234.         $availableSelections = [];
  1235.         $positions count($range) - $selectedCount 1;
  1236.         for ($shift 0$shift $positions$shift++) {
  1237.             $selection array_slice($range$shift$selectedCount);
  1238.             $rowGroups = [];
  1239.             foreach (array_keys($this->rowGroups__) as $index) {
  1240.                 $rowGroups[$index] = array_filter(
  1241.                     $this->rowGroups__[$index],
  1242.                     function (Seat $item) use ($selection) {
  1243.                         return !array_filter(
  1244.                             $selection,
  1245.                             function (Seat $element) use ($selection$item) {
  1246.                                 return $element === $item;
  1247.                             }
  1248.                         );
  1249.                     }
  1250.                 );
  1251.             }
  1252.             $this->rowGroupsSingletons__ $rowGroupsSingletons array_values(array_map(
  1253.                 function ($item) {
  1254.                     return array_shift($item);
  1255.                 },
  1256.                 array_filter(
  1257.                     $rowGroups,
  1258.                     function ($item) {
  1259.                         return count($item ?: []) == 1;
  1260.                     }
  1261.                 )
  1262.             ));
  1263.             if ($this->okSelection($row$selection)) {
  1264.                 // if a wheelchair is the selection result and originally NO WHEELCHAIR was selected, the result is not valid
  1265.                 if (false === ($selection[0]->getOriginalStatus() === && $selectedSeat->getOriginalStatus() !== 3)) {
  1266.                     $availableSelections[] = $selection;
  1267.                 }
  1268.             }
  1269.         }
  1270.         // error_log(sprintf("%s %s(%s) count(availableSelections) ", date('Y-m-d H:i:s'), __METHOD__, __LINE__) . var_export(count($availableSelections), true));
  1271.         usort($availableSelections, function ($a$b) use ($selectedSeat) {
  1272.             $aDistance abs($a[0]->getNormalizedColumnIndex() - $selectedSeat->getNormalizedColumnIndex());
  1273.             $bDistance abs($b[0]->getNormalizedColumnIndex() - $selectedSeat->getNormalizedColumnIndex());
  1274.             if ($aDistance $bDistance) {
  1275.                 return -1;
  1276.             } elseif ($aDistance $bDistance) {
  1277.                 return 1;
  1278.             }
  1279.             return 0;
  1280.         });
  1281.         if ($availableSelections) {
  1282.             return array_shift($availableSelections);
  1283.         }
  1284.         return [];
  1285.     }
  1286.     /**
  1287.      * @param Seat[] $row
  1288.      * @param Seat[] $selection
  1289.      *
  1290.      * @return bool
  1291.      */
  1292.     protected function okSelection(array $row, array $selection)
  1293.     {
  1294.         $resultItems = [
  1295.             $this->hasErrorSelectionForSingleEdgeSeats($row$selection),
  1296.             $this->hasErrorSelectionForCoronaDistance($row$selection),
  1297.             $this->hasErrorSelectionForIncompleteDoubleSeats($row$selection),
  1298.         ];
  1299.         $this->logger->debug(sprintf(
  1300.             'SingleEdgeSeatError: %s, CoronaDistanceError: %s, IncompleteDoubleSeatsError: %s',
  1301.             var_export($resultItems[0], true),
  1302.             var_export($resultItems[1], true),
  1303.             var_export($resultItems[2], true)
  1304.         ));
  1305.         $preResult 1;
  1306.         foreach ($resultItems as $item) {
  1307.             $preResult *= intval(!$item);
  1308.         }
  1309.         return boolval($preResult);
  1310.     }
  1311.     /**
  1312.      * @param Seat[] $row
  1313.      * @param Seat[] $selection
  1314.      *
  1315.      * @return bool
  1316.      */
  1317.     protected function hasErrorSelectionForIncompleteDoubleSeats(array $row, array $selection): bool
  1318.     {
  1319.         $groupsList = [];
  1320.         foreach ($row as $item) {
  1321.             if (!empty($item->getDoubleSeatId())) {
  1322.                 $groupsList[$item->getDoubleSeatId()][] = $item;
  1323.             }
  1324.         }
  1325.         foreach ($groupsList as $groupId => $group) {
  1326.             $freeCount 0;
  1327.             foreach ($group as $item) {
  1328.                 $isSeatFree in_array($item->getStatusCalculated(), self::FREE_STATUSES);
  1329.                 $isSeatSelected array_filter($selection, function ($element) use ($item) {
  1330.                     return $this->isSeatPositionEq($element$item);
  1331.                 });
  1332.                 if ($isSeatFree && !$isSeatSelected) {
  1333.                     ++$freeCount;
  1334.                 }
  1335.             }
  1336.             if (=== $freeCount) {
  1337.                 return true;
  1338.             }
  1339.         }
  1340.         return false;
  1341.     }
  1342.     /**
  1343.      * @param Seat[] $row
  1344.      * @param Seat[] $selection
  1345.      *
  1346.      * @return bool
  1347.      */
  1348.     protected function hasErrorSelectionForSingleEdgeSeats(array $row, array $selection): bool
  1349.     {
  1350.         if ($this->restrictions->getApplyCoronaDistance()) {
  1351.             // check only if corona distance does not apply
  1352.             return false;
  1353.         }
  1354.         $expressionLanguage = new ExpressionLanguage();
  1355.         $rowIndexed = [];
  1356.         foreach ($row as $item) {
  1357.             $rowIndexed[$item->getNormalizedColumnIndex()] = $item;
  1358.         }
  1359.         $selectionIndexes = [];
  1360.         foreach ($selection as $item) {
  1361.             $selectionIndexes[] = $item->getNormalizedColumnIndex();
  1362.         }
  1363.         $max max($selectionIndexes);
  1364.         $maxSeat0 $rowIndexed[$max] ?? null;
  1365.         $maxSeat1 $rowIndexed[$max 1] ?? null;
  1366.         $maxSeat2 $rowIndexed[$max 2] ?? null;
  1367.         $min min($selectionIndexes);
  1368.         $minSeat0 $rowIndexed[$min] ?? null;
  1369.         $minSeat1 $rowIndexed[$min 1] ?? null;
  1370.         $minSeat2 $rowIndexed[$min 2] ?? null;
  1371.         // zero is the selected seat, first and second are the next and nextnext (left or right)
  1372.         $check = function (?Seat $zero, ?Seat $first, ?Seat $secondbool $syncDirections) use ($expressionLanguage) {
  1373.             if (!$first) {
  1374.                 return false;
  1375.             }
  1376.             $eval $expressionLanguage->evaluate(
  1377.                 sprintf('first %s zero'$syncDirections '>' '<'),
  1378.                 [
  1379.                     'first' => $first->getColumnIndex(),
  1380.                     'zero' => $zero->getColumnIndex(),
  1381.                 ]
  1382.             );
  1383.             $indexCheckFirst = ($second && abs($second->getColumnIndex() - $first->getColumnIndex()) > 1);
  1384.             $indexCheckZero = ($eval && $first->getOriginalStatus() !== && abs($first->getColumnIndex() - $zero->getColumnIndex()) == 1);
  1385.             $statusCheck =  ($second && !in_array($second->getStatusCalculated(), self::FREE_STATUSES));
  1386.             $freeStatus in_array($first->getStatusCalculated(), self::FREE_STATUSES);
  1387.             $notSame = !array_filter($this->rowGroupsSingletons__, function ($item) use ($first) {
  1388.                 return $item == $first;
  1389.             });
  1390.             $result $indexCheckZero
  1391.                 &&
  1392.                 $freeStatus
  1393.                 &&
  1394.                 $notSame
  1395.                 && (!$second || $indexCheckFirst || $statusCheck);
  1396.             
  1397.             return $result;
  1398.         };
  1399.         $row or $row = [];
  1400.         $currentDirection = (function (array $row) {
  1401.             if (count($row) < 2) {
  1402.                 return null;
  1403.             }
  1404.             /** @var Seat $zero */
  1405.             $zero $row[0];
  1406.             /** @var Seat $first */
  1407.             $first $row[1];
  1408.             return (0
  1409.                 || $first->getColumnIndex() >= $zero->getColumnIndex()
  1410.                 || (1
  1411.                     && $first->getColumnIndex() < $zero->getColumnIndex()
  1412.                     && intval($first->getAreaCategoryCode()) != intval($zero->getAreaCategoryCode()))) ? 'LEFT' 'RIGHT';
  1413.         })($row);
  1414.         $resultLeft $check($maxSeat0$maxSeat1$maxSeat2$currentDirection == 'LEFT');
  1415.         $resultRight $check($minSeat0$minSeat1$minSeat2$currentDirection == 'RIGHT');
  1416.         $result $resultLeft || $resultRight;
  1417.         return $result;
  1418.     }
  1419.     /**
  1420.      * @param Seat[]    $row
  1421.      * @param Seat[] $selection
  1422.      *
  1423.      * @return bool
  1424.      */
  1425.     protected function hasErrorSelectionForCoronaDistance(array $row, array $selection): bool
  1426.     {
  1427.         if (!$this->restrictions->getApplyCoronaDistance()) {
  1428.             // check only if we have to apply corona distance
  1429.             return false;
  1430.         }
  1431.         $expressionLanguage = new ExpressionLanguage();
  1432.         $rowIndexed = [];
  1433.         foreach ($row as $item) {
  1434.             $rowIndexed[$item->getNormalizedColumnIndex()] = $item;
  1435.         }
  1436.         $selectionIndexes = [];
  1437.         foreach ($selection as $item) {
  1438.             $selectionIndexes[] = $item->getNormalizedColumnIndex();
  1439.         }
  1440.         $max max($selectionIndexes);
  1441.         $maxSeat0 $rowIndexed[$max] ?? null;
  1442.         $maxSeat1 $rowIndexed[$max 1] ?? null;
  1443.         $maxSeat2 $rowIndexed[$max 2] ?? null;
  1444.         $min min($selectionIndexes);
  1445.         $minSeat0 $rowIndexed[$min] ?? null;
  1446.         $minSeat1 $rowIndexed[$min 1] ?? null;
  1447.         $minSeat2 $rowIndexed[$min 2] ?? null;
  1448.         $check = function (?Seat $zero, ?Seat $first, ?Seat $secondbool $syncDirections) use ($expressionLanguage) {
  1449.             if (!$first) {
  1450.                 return false;
  1451.             }
  1452.             $eval $expressionLanguage->evaluate(
  1453.                 sprintf('first %s zero'$syncDirections '>' '<'),
  1454.                 [
  1455.                     'first' => $first->getColumnIndex(),
  1456.                     'zero' => $zero->getColumnIndex(),
  1457.                 ]
  1458.             );
  1459.             $nextOneIsSeat = ($eval && abs($first->getColumnIndex() - $zero->getColumnIndex()) == 1);
  1460.             return $nextOneIsSeat
  1461.                 &&
  1462.                 in_array($first->getStatusCalculated(), self::OCCUPIED_STATUSES)
  1463.                 &&
  1464.                 !array_filter($this->rowGroupsSingletons__, function ($item) use ($first) {
  1465.                     return $item == $first;
  1466.                 })
  1467.                 /*
  1468.                 && (!$second
  1469.                     || ($second && abs($second->getColumnIndex() - $first->getColumnIndex()) > 1)
  1470.                     || ($second && !in_array($second->getStatusCalculated(), self::OCCUPIED_STATUSES))
  1471.                 )
  1472.                 */;
  1473.         };
  1474.         $row or $row = [];
  1475.         $currentDirection = (function (array $row) {
  1476.             if (count($row) < 2) {
  1477.                 return null;
  1478.             }
  1479.             /** @var Seat $zero */
  1480.             $zero $row[0];
  1481.             /** @var Seat $first */
  1482.             $first $row[1];
  1483.             return (0
  1484.                 || $first->getColumnIndex() >= $zero->getColumnIndex()
  1485.                 || (1
  1486.                     && $first->getColumnIndex() < $zero->getColumnIndex()
  1487.                     && intval($first->getAreaCategoryCode()) != intval($zero->getAreaCategoryCode()))) ? 'LEFT' 'RIGHT';
  1488.         })($row);
  1489.         return 0
  1490.             || $check($maxSeat0$maxSeat1$maxSeat2$currentDirection == 'LEFT')
  1491.             || $check($minSeat0$minSeat1$minSeat2$currentDirection == 'RIGHT');
  1492.     }
  1493.     /**
  1494.      * @param Seat[]    $row
  1495.      * @param Seat      $selectedSeat
  1496.      * @param callable  $getter
  1497.      * @param int|null  $minRangeLength
  1498.      * @param bool|null $filterFirst
  1499.      *
  1500.      * @return array
  1501.      */
  1502.     protected function extractRanges(array $rowSeat $selectedSeat, callable $getterint $minRangeLength 1bool $filterFirst true)
  1503.     {
  1504.         $filter = function (Seat $item) use ($selectedSeat) {
  1505.             if (
  1506.                 in_array($item->getStatusCalculated(), self::FREE_STATUSES)
  1507.                 && (strpos($item->getId(), 'R') !== false
  1508.                     || strpos($selectedSeat->getId(), 'R') !== false
  1509.                     || intval($item->getAreaCategoryCode()) == intval($selectedSeat->getAreaCategoryCode()))
  1510.             ) {
  1511.                 return true;
  1512.             }
  1513.             return false;
  1514.         };
  1515.         $filtered $filterFirst
  1516.             array_filter($row$filter)
  1517.             : $row;
  1518.         $result $this->splitRow($filtered$getter$minRangeLength);
  1519.         if (!$filterFirst) {
  1520.             foreach (array_keys($result) as $index) {
  1521.                 $result[$index] = array_values(array_filter($result[$index], $filter));
  1522.             }
  1523.         }
  1524.         return $result;
  1525.     }
  1526.     protected function splitRow($row$getter$minRangeLength)
  1527.     {
  1528.         $preResult = [];
  1529.         $item = [];
  1530.         /** @var Seat $prev */
  1531.         $prev null;
  1532.         /**
  1533.          * @var Seat $seat
  1534.          */
  1535.         foreach ($row as $index => $seat) {
  1536.             $diff intval(abs($getter($index$seat) - intval($prev)));
  1537.             if (
  1538.                 !is_null($prev) && ($diff || $diff == 0)
  1539.             ) {
  1540.                 $preResult[] = $item;
  1541.                 $item = [];
  1542.             }
  1543.             $item[] = $seat;
  1544.             $prev $getter($index$seat);
  1545.         }
  1546.         $preResult[] = $item;
  1547.         return array_values(array_filter($preResult, function ($item) use ($minRangeLength) {
  1548.             if (count($item) >= $minRangeLength) {
  1549.                 return true;
  1550.             }
  1551.             return false;
  1552.         }));
  1553.     }
  1554.     protected function getWalkThroughIndexes($indexes$touchIndex)
  1555.     {
  1556.         $indexes or $indexes = [];
  1557.         if (is_null($touchIndex)) {
  1558.             return [];
  1559.         }
  1560.         $key array_search($touchIndex$indexes);
  1561.         if ($key === false) {
  1562.             return [];
  1563.         }
  1564.         $listA array_reverse(array_slice($indexes0$key));
  1565.         $listB = [];
  1566.         (count($indexes) > $key 1) and $listB array_slice($indexes$key 1);
  1567.         return array_merge([$key], $this->mixLists($listA$listB));
  1568.     }
  1569.     protected function mixLists($listA$listB)
  1570.     {
  1571.         $count max(count($listA), count($listB));
  1572.         $result = [];
  1573.         for ($index 0$index $count$index++) {
  1574.             foreach ([$listA$listB] as $item) {
  1575.                 isset($item[$index]) and $result[] = $item[$index];
  1576.             }
  1577.         }
  1578.         return $result;
  1579.     }
  1580.     /**
  1581.      * Returns normalized seat plan representation [<Row> => [<Seat>, ...]]
  1582.      *
  1583.      * @param Row[]     $seatPlanRows
  1584.      * @param Seat|null $selectedSeat
  1585.      * @param Seat[]    $selection
  1586.      *
  1587.      * @return array<Seat|int|array<Seat>>
  1588.      */
  1589.     public function getNormalized(array $seatPlanRows, ?Seat $selectedSeat null, array $selection = []): array
  1590.     {
  1591.         $rows = [];
  1592.         foreach ($seatPlanRows as $row) {
  1593.             $rows[$row->getPhysicalName()] = array_merge($rows[$row->getPhysicalName()] ?? [], $row->getSeats());
  1594.         }
  1595.         $max = !$rows from($rows)
  1596.             ->max(function ($item) {
  1597.                 return count($item ?: []);
  1598.             });
  1599.         $seats = [];
  1600.         foreach ($rows as $index => $item) {
  1601.             $unitLength 100 $max;
  1602.             usort($item, function (Seat $aSeat $b) use ($unitLength) {
  1603.                 $aDist = ($a->getColumnIndex() * $unitLength) + floatval($a->getRowRight());
  1604.                 $bDist = ($b->getColumnIndex() * $unitLength) + floatval($b->getRowRight());
  1605.                 if ($aDist $bDist) {
  1606.                     return -1;
  1607.                 } elseif ($aDist $bDist) {
  1608.                     return 1;
  1609.                 }
  1610.                 return 0;
  1611.             });
  1612.             $seats[$index] = $item;
  1613.         }
  1614.         //if($seats && array_values($seats)[0])error_log(sprintf("%s %s(%s) ", date('Y-m-d H:i:s'), __METHOD__, __LINE__) . var_export(array_values($seats)[0][0], true));
  1615.         $preResult = [];
  1616.         foreach ($seats as $index => $rowSeats) {
  1617.             $item = [];
  1618.             /** @var Seat $seat */
  1619.             foreach ($rowSeats as $seat) {
  1620.                 $item[] = $seat;
  1621.                 $seat->setRowName($index);
  1622.                 if ($this->isSeatPositionEq($selectedSeat$seat)) {
  1623.                     $selectedSeat $seat;
  1624.                 }
  1625.                 foreach ($selection as $selectionItem) {
  1626.                     if ($this->isSeatPositionEq($selectionItem$seat)) {
  1627.                         $seat->setStatus(99);
  1628.                     }
  1629.                 }
  1630.             }
  1631.             $preResult[$index] = array_values($item);
  1632.         }
  1633.         $result array_values($preResult);
  1634.         // error_log(sprintf("%s %s(%s) ", date('Y-m-d H:i:s'), __METHOD__, __LINE__) . var_export(
  1635.         // function($result){
  1636.         // $seats = [];
  1637.         // foreach ($result as $rowIndex => $row) {
  1638.         // if($row[0]->getPosition()->getRowIndex() == 8 || $rowIndex == 8) {
  1639.         // foreach ($row as $columnIndex => $seat) {
  1640.         // if($seat->getStatus() == 99) {
  1641.         // $seats[] = $seat;
  1642.         // }
  1643.         // }
  1644.         // }
  1645.         // }
  1646.         // return $seats;
  1647.         // }, true));
  1648.         foreach ($result as $rowIndex => $row) {
  1649.             /**
  1650.              * @var Seat $item
  1651.              */
  1652.             foreach ($row as $columnIndex => $item) {
  1653.                 $item->setNormalizedRowIndex($rowIndex);
  1654.                 $item->setNormalizedColumnIndex($columnIndex); //);$item->getPosition()->getColumnIndexVista()
  1655.             }
  1656.         }
  1657.         //if($result && $result[0])error_log(sprintf("%s %s(%s) ", date('Y-m-d H:i:s'), __METHOD__, __LINE__) . var_export($result[0][0], true));
  1658.         return [$result$selectedSeat$selection$max];
  1659.     }
  1660.     public function isSeatPositionEq(?Seat $a, ?Seat $b): bool
  1661.     {
  1662.         if (!$a || !$b) {
  1663.             return false;
  1664.         }
  1665.         if (
  1666.             $a->getPosition()->getColumnIndex() === $b->getPosition()->getColumnIndex()
  1667.             && $a->getPosition()->getRowIndex() === $b->getPosition()->getRowIndex()
  1668.             && $a->getPosition()->getAreaNumber() === $b->getPosition()->getAreaNumber()
  1669.         ) {
  1670.             return true;
  1671.         }
  1672.         return false;
  1673.     }
  1674.     /**
  1675.      * @param array<int, array<Seat>> $normalized
  1676.      * @param OrderSeat[]             $orderSeats
  1677.      *
  1678.      * @return array
  1679.      */
  1680.     public function getSeatsFromSeatPlan(array $normalized, array $orderSeats): array
  1681.     {
  1682.         $result = [];
  1683.         foreach ($orderSeats as $orderSeat) {
  1684.             $seat = (new Seat())->setPosition(
  1685.                 (new SeatPosition())
  1686.                     ->setAreaNumber($orderSeat->getAreaNumber())
  1687.                     ->setRowIndex($orderSeat->getRowIndex())
  1688.                     ->setColumnIndex($orderSeat->getColumnIndex())
  1689.             );
  1690.             foreach ($normalized as $row) {
  1691.                 foreach ($row as $item) {
  1692.                     if ($this->isSeatPositionEq($item$seat)) {
  1693.                         $result[] = [$orderSeat$item];
  1694.                         continue 2;
  1695.                     }
  1696.                 }
  1697.             }
  1698.         }
  1699.         return $result;
  1700.     }
  1701.     /**
  1702.      * @param Row[]  $rows
  1703.      * @param Seat[] $selection
  1704.      *
  1705.      * @return void
  1706.      */
  1707.     public function cleanUpSelection(array $rows, array $selection)
  1708.     {
  1709.         /** @var Row[] $filteredRows */
  1710.         $filteredRows = [];
  1711.         foreach ($rows as $item) {
  1712.             if ($item->getPhysicalName() === (string) $selection[0]->getRowName()) {
  1713.                 $filteredRows[] = $item;
  1714.             }
  1715.         }
  1716.         foreach ($filteredRows as $row) {
  1717.             foreach ($row->getSeats() as $seat) {
  1718.                 foreach ($selection as $selectionItem) {
  1719.                     if ($this->isSeatPositionEq($seat$selectionItem)) {
  1720.                         $seat->setStatus((strpos($seat->getId(), 'R') === false) ? 3);
  1721.                         continue;
  1722.                     }
  1723.                 }
  1724.             }
  1725.         }
  1726.     }
  1727.     /**
  1728.      * @param string      $seatPlanInput
  1729.      * @param string      $cinemaId
  1730.      * @param string      $screenNumber
  1731.      * @param             $seatPlanExists
  1732.      * @param string      $sessionId
  1733.      *
  1734.      * @return string
  1735.      */
  1736.     public function processRaw(string $seatPlanInputstring $cinemaIdstring $screenNumber$seatPlanExistsstring $sessionId)
  1737.     {
  1738.         //$seatPlanTree = $this->serializer->deserialize($seatPlanInput, SeatPlan::class, 'json');
  1739.         $seatPlanTree json_decode($seatPlanInput);
  1740.         $this->setSeatPlanIcons($seatPlanTree$cinemaId);
  1741.         $this->reorderAreas($seatPlanTree);
  1742.         //$seatPlanTree->setScreenNumber($screenNumber);
  1743.         //$seatPlanTree->setSeatPlanExists($seatPlanExists);
  1744.         //$seatPlanTree->setCinemaId($cinemaId);
  1745.         //$seatPlanOutput = $this->serializer->serialize($seatPlanTree, 'json');
  1746.         //$seatPlanOutput = preg_replace('/(.+)(\\}\s*)$/', "$1,\"screenNumber\":\"$screenNumber\",\"seatPlanExists\":\"$seatPlanExists\",\"cinemaId\":\"$cinemaId\"$2", $seatPlanInput);
  1747.         $seatPlanTree->SeatLayoutData->screenNumber $screenNumber;
  1748.         $seatPlanTree->SeatLayoutData->seatPlanExists $seatPlanExists;
  1749.         $seatPlanTree->SeatLayoutData->cinemaId $cinemaId;
  1750.         $seatPlanTree->SeatLayoutData->sessionId $sessionId;
  1751.         foreach ($seatPlanTree->SeatLayoutData->Areas as &$area) {
  1752.             $this->setVistaColumns($area->Rows);
  1753.         }
  1754.         //error_log(sprintf("%s %s(%s) ", date('Y-m-d H:i:s'), __METHOD__, __LINE__) . ' seatPlanTree ' . var_export($seatPlanTree, true));
  1755.         $seatPlanOutput json_encode($seatPlanTree);
  1756.         return $seatPlanOutput;
  1757.     }
  1758.     public function reorderAreas(&$seatPlanTree)
  1759.     {
  1760.         usort($seatPlanTree->SeatLayoutData->Areas, function ($a$b) {
  1761.             $aMax $this->getMaxRowName($a->Rows);
  1762.             $bMax =  $this->getMaxRowName($b->Rows);
  1763.             return $aMax $bMax : ($aMax $bMax ? -0);
  1764.         });
  1765.         $top floatval(0);
  1766.         foreach ($seatPlanTree->SeatLayoutData->Areas as &$area) {
  1767.             $area->Top $top;
  1768.             $top += floatval($area->Height);
  1769.         }
  1770.     }
  1771.     public function getMaxRowName($rows)
  1772.     {
  1773.         $rowMax ' ';
  1774.         foreach ($rows as $row) {
  1775.             if ($rowMax $row->PhysicalName) {
  1776.                 $rowMax $row->PhysicalName;
  1777.             }
  1778.         }
  1779.         return $rowMax;
  1780.     }
  1781.     public function reservationsToSaved2(&$rows, &$rowsSaved$rowsMax)
  1782.     {
  1783.         //error_log(sprintf("%s %s(%s) ", date('Y-m-d H:i:s'), __METHOD__, __LINE__) . ' count($rows) ' . count($rows) . ' count($rowsSaved) ' . count($rowsSaved));
  1784.         //return $rows; // !!!!!!!!!!!!!!!!!!!!!!!!!!!
  1785.         error_log(sprintf("%s %s(%s) rows %d/%d added  "date('Y-m-d H:i:s'), __METHOD____LINE__count($rows), count($rowsSaved)));
  1786.         // join rows with the same physical name
  1787.         $previousRow null;
  1788.         $preparedRows = [];
  1789.         foreach ($rows as &$row) {
  1790.             if ($previousRow == null) {
  1791.                 $previousRow $row;
  1792.             } else if ($previousRow->getPhysicalName() != $row->getPhysicalName()) {
  1793.                 $preparedRows[] = $previousRow;
  1794.                 $previousRow $row;
  1795.             } else {
  1796.                 // get right from saved rows
  1797.                 // move seats from next row to previous 
  1798.                 //error_log(sprintf("%s %s(%s) before previousRow %d row %d", date('Y-m-d H:i:s'), __METHOD__, __LINE__, count($previousRow->getSeats()), count($row->getSeats())));
  1799.                 //$rowPair = $previousRow->getRight() < $row->getRight() ? [$previousRow, $row]: [$row, $previousRow];
  1800.                 if ($previousRow->getRight() < $row->getRight()) {
  1801.                     $rowPair = [$previousRow$row];
  1802.                 } else {
  1803.                     $rowPair = [$row$previousRow];
  1804.                     $previousRowTemp $previousRow;
  1805.                     $previousRow $row;
  1806.                     $row $previousRowTemp;
  1807.                 }
  1808.                 $seats = [];
  1809.                 foreach ($rowPair as $rowPairEl) {
  1810.                     foreach ($rowPairEl->getSeats() as $s) {
  1811.                         $seats[] = $s;
  1812.                     }
  1813.                 }
  1814.                 $seeatCountBefore count($previousRow->getSeats());
  1815.                 $previousRow->setSeats($seats);
  1816.                 error_log(sprintf("%s %s(%s) seeatCountBefore {$seeatCountBefore} and after previousRow %d"date('Y-m-d H:i:s'), __METHOD____LINE__count($previousRow->getSeats())));
  1817.             }
  1818.         }
  1819.         if ($previousRow) {
  1820.             $preparedRows[] = $previousRow;
  1821.         }
  1822.         $rows $preparedRows;
  1823.         error_log(sprintf("%s %s(%s) row count after join %d"date('Y-m-d H:i:s'), __METHOD____LINE__count($rows)));
  1824.         //if($columnIndex=call_user_func(function($rows){$rowFirst=null;foreach($rows as $row){if($seats=$row->getSeats()){return $seats[0]->getPosition()->getColumnIndex();}}},$rows) || true){error_log(sprintf("%s %s(%s) columnIndex $columnIndex", date('Y-m-d H:i:s'), __METHOD__, __LINE__));}
  1825.         foreach ($rows as &$row) {
  1826.             if ($row->getPhysicalName() == 13error_log(sprintf("%s %s(%s) getPhysicalName {$row->getPhysicalName()}"date('Y-m-d H:i:s'), __METHOD____LINE__));
  1827.             if ($seats $row->getSeats()) {
  1828.                 foreach ($row->getSeats() as &$seat) {
  1829.                     foreach ($rowsSaved as $rowSaved) {
  1830.                         if ($rowSaved->getSeats()) {
  1831.                             foreach ($rowSaved->getSeats() as $seatSaved) {
  1832.                                 if (
  1833.                                     $seat->getPosition()->getRowIndex() == $seatSaved->getPosition()->getRowIndex() &&
  1834.                                     $seat->getPosition()->getAreaNumber() == $seatSaved->getPosition()->getAreaNumber() &&
  1835.                                     $seat->getPosition()->getColumnIndex() == $seatSaved->getPosition()->getColumnIndexVista()
  1836.                                 ) {
  1837.                                     if (($ri $seatSaved->getPosition()->getColumnIndexVista()) != ($ci $seatSaved->getPosition()->getColumnIndexRender()) && $seat->getStatus() == 99)
  1838.                                         error_log(sprintf("%s %s(%s) row {$seat->getPosition()->getRowIndex()} vista $ri render $ci"date('Y-m-d H:i:s'), __METHOD____LINE__)); // . " vista {$seatSaved->getPosition()->getColumnIndexVista()} render {$seatSaved->getPosition()->getColumnIndexRender()}"
  1839.                                     //$seat->setColumnIndex($seatSaved->getPosition()->getColumnIndexRender());
  1840.                                     $seat->getPosition()->setColumnIndex($seatSaved->getPosition()->getColumnIndexRender());
  1841.                                     $seat->getPosition()->setColumnIndexVista($seatSaved->getPosition()->getColumnIndexVista());
  1842.                                     $seat->getPosition()->setColumnIndexRender($seatSaved->getPosition()->getColumnIndexRender());
  1843.                                     $seat->setSeatsInGroup($seatSaved->getSeatsInGroup());
  1844.                                     // if(($ri = $seatSaved->getPosition()->getRowIndex()) == 11 && ($ci = $seat->getPosition()->getColumnIndex()) > 10)error_log(sprintf("%s %s(%s) row $ri column $ci", date('Y-m-d H:i:s'), __METHOD__, __LINE__) . 
  1845.                                     // " vista {$seat->getPosition()->getColumnIndexVista()} render {$seat->getPosition()->getColumnIndexRender()}");
  1846.                                     break;
  1847.                                 }
  1848.                             }
  1849.                         }
  1850.                     }
  1851.                 }
  1852.             }
  1853.         }
  1854.         //return $rows; // !!!!!!!!!!!!!!!!!!!!!!!!!!!
  1855.         // join rows with equal physical names
  1856.         // $rowsJoind = [];
  1857.         // foreach($rows as &$row) {
  1858.         // if($physicalName = $row->getPhysicalName()) {
  1859.         // $rowAdded = null;
  1860.         // sear for row with this physical name
  1861.         // foreach($rowsJoind as &$rowJoind) {
  1862.         // if($rowJoind->getPhysicalName() == $physicalName) {
  1863.         // $rowAdded = $rowJoind;
  1864.         // break;
  1865.         // }
  1866.         // }
  1867.         // if($rowAdded) {
  1868.         // if physical name already exists - join this row to it
  1869.         // $rowJoind->setSeats($rowJoind->getSeats() + $row->getSeats());
  1870.         // $rowJoind->setRight(min($rowJoind->getRight(), $row->getRight()));
  1871.         // } else {
  1872.         // if first row with this physical name - add it
  1873.         // $rowsJoind[] = $row;
  1874.         // }
  1875.         // } else {
  1876.         // if empty row - add it
  1877.         // $rowsJoind[] = $row;
  1878.         // }
  1879.         // }
  1880.         // if extra empty rows in the saved data - add them
  1881.         $rowsJoind $rows;
  1882.         $preparedRows = [];
  1883.         $rowsIndex 0;
  1884.         $rowsSavedIndex 0;
  1885.         $currentRowPosition 0;
  1886.         $currentRowSavedPosition 0;
  1887.         while ($currentRowPosition count($rowsJoind) || $currentRowSavedPosition count($rowsSaved)) {
  1888.             // if current saved row is empty - add empty row and go to next
  1889.             if ($currentRowSavedPosition count($rowsSaved) && count($rowsSaved[$currentRowSavedPosition]->getSeats()) == 0) {
  1890.                 $preparedRows[] = new Row();
  1891.                 $currentRowSavedPosition++;
  1892.                 continue;
  1893.             }
  1894.             // if current vista row is empty - go to next row
  1895.             if ($currentRowPosition count($rowsJoind) && count($rowsJoind[$currentRowPosition]->getSeats()) == 0) {
  1896.                 $currentRowPosition++;
  1897.                 continue;
  1898.             }
  1899.             // if both rows exists - add vista row
  1900.             if ($currentRowPosition count($rowsJoind) && $currentRowSavedPosition count($rowsSaved)) {
  1901.                 $preparedRows[] = $rowsJoind[$currentRowPosition];
  1902.                 if ($rowsJoind[$currentRowPosition]->getPhysicalName() != $rowsSaved[$currentRowSavedPosition]->getPhysicalName()) {
  1903.                     error_log(sprintf("%s %s(%s) different physical names rowsJoind[$currentRowPosition] %s rowsSaved[$currentRowSavedPosition] %s "date('Y-m-d H:i:s'), __METHOD____LINE__$rowsJoind[$currentRowPosition]->getPhysicalName(), $rowsSaved[$currentRowSavedPosition]->getPhysicalName()));
  1904.                 }
  1905.                 $currentRowSavedPosition++;
  1906.                 $currentRowPosition++;
  1907.             }
  1908.         }
  1909.         error_log(sprintf("%s %s(%s) row count final %d"date('Y-m-d H:i:s'), __METHOD____LINE__count($preparedRows)));
  1910.         return $preparedRows;
  1911.     }
  1912. }