<?php
/** 
 * @package     VikAppointments
 * @subpackage  core
 * @author      E4J s.r.l.
 * @copyright   Copyright (C) 2021 E4J s.r.l. All Rights Reserved.
 * @license     http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
 * @link        https://vikwp.com
 */

// No direct access
defined('ABSPATH') or die('No script kiddies please!');

VAPLoader::import('libraries.availability.timeline.parser.group');

/**
 * Extended group timeline parser.
 *
 * @since 1.7.8
 */
class VAPAvailabilityTimelineParserGroupext extends VAPAvailabilityTimelineParserGroup
{
	/**
	 * Helper method used to elaborate the resulting timeline.
	 *
	 * @param 	string 	 $date      The UTC date in military format.
	 * @param 	integer  $people    The number of participants.
	 * @param 	array    $worktime  A list of working times.
	 * @param 	array    $bookings  A list of booked appointments.
	 * @param 	object   $service   The service details.
	 * @param 	mixed    &$seats    An array containing the remaining seats for each time.
	 *
	 * @return 	array    The resulting timeline.
	 */
	protected function elaborateTimeLine($date, $people, $worktime, $bookings, $service, &$seats = null)
	{
		$arr = [];
		$seats = $seats ?? [];

		// default one specified by the configuration
		$min_int = VAPFactory::getConfig()->getUint('minuteintervals');
		
		for ($i = 0; $i < count($worktime); $i++)
		{
			for ($j = $worktime[$i]->fromts; ($j + $min_int) <= $worktime[$i]->endts; $j += $min_int)
			{
				$arr[$i][$j] = 1;
			}
		}

		// fetch default system timezone
		$tz = JFactory::getApplication()->get('offset', 'UTC');

		// re-format check-in date for correct comparison
		$ymd = JDate::getInstance($date)->format('Y-m-d');

		$cont_people = 0;
		
		foreach ($bookings as $k => $b)
		{
			if (!$b->timezone)
			{
				// use default system timezone
				$b->timezone = $tz;
			}
			
			// increase people count
			$cont_people += $b->people_count;

			if ($k == count($bookings) - 1 || $bookings[$k + 1]->checkin_ts != $b->checkin_ts || $bookings[$k + 1]->timezone != $b->timezone)
			{
				// create check-in and adjust it to the employee timezone
				$checkin = new JDate($b->checkin_ts);
				$checkin->setTimezone(new DateTimeZone($b->timezone));

				// get check-in time adjusted to local timezone
				$from = $checkin->format('H:i', $local = true);
				// convert time string in minutes
				$from = JHtml::fetch('vikappointments.time2min', $from);

				/**
				 * Check the day factor of a reservation to check if it is referring
				 * to the current day or if it close to the bounds of this working time.
				 * Used to support midnight reservations.
				 *
				 * @since 1.6
				 */

				if ($checkin->format('Y-m-d', $local = true) > $ymd)
				{
					// we are evaluating a reservation for the next day, so we need to increase the 
					// initial time by 1440 minutes (24 hours * 60).
					$from += 1440;
				}
				else if ($checkin->format('Y-m-d', $local = true) < $ymd)
				{
					// we are evaluating a reservation for the previous day, so we need to decrease the 
					// initial time by 1440 minutes (24 hours * 60).
					$from -= 1440;
				}

				// calculate end time by adding total duration to start time
				$to = $from + $b->duration + $b->sleep;

				for ($i = $from; $i < $to; $i += $min_int)
				{
					$found = false;

					for ($j = 0; $j < count($arr) && !$found; $j++)
					{
						// calculate the number of occupied seats
						$real_occupancy = $service->max_capacity - ($seats[$i] ?? $service->max_capacity) + $cont_people;

						/**
						 * Try to block appointments that come from a different service or
						 * if the number of people exceeds the total capacity.
						 *
						 * @since 1.6 	check if the services are different only if $arr[$j][$i] is set
						 */
						if (!empty($arr[$j][$i]) && ($real_occupancy + $people > $service->max_capacity || $b->id_service != $service->id || $b->closure))
						{
							$found = true;
							$arr[$j][$i] = 0;
						}

						if ($b->id_service == $service->id && !$b->closure)
						{
							if (!isset($seats[$i]))
							{
								$seats[$i] = $service->max_capacity;
							}

							// same service, we can display the remaining seats
							$seats[$i] = max(0, $seats[$i] - $cont_people);
						}
						else
						{
							// booked for a different service, unset the remaining seats
							$seats[$i] = 0;
						}
					}

					/**
					 * We may have different services that display shifted
					 * timelines. This would cause an issue as previous check
					 * ignores the times that don't match the evaluated slots.
					 *
					 * We need to unset here all the times that intersect with
					 * an existing reservation, which might have been created for
					 * a different service.
					 *
					 * @since 1.6.2
					 */
					if (!$found)
					{
						// find all slots that intersect this one
						for ($j = 0; $j < count($arr); $j++)
						{
							foreach ($arr[$j] as $arr_hm => &$v)
							{
								if (($from < $arr_hm && $arr_hm < $to)
									|| ($arr_hm < $from && $from < $arr_hm + $service->duration + $service->sleep))
								{
									/**
									 * @todo evaluate to enhance this statement, because in case
									 *       an appointment uses an increased duration, the exceeding
									 *       slot are entirely turned off
									 */
									// $v = 0;
								}
							}
						}
					}
				}
				
				$cont_people = 0;
			}
		}
		
		$n_step = $service->duration + $service->sleep;

		// array deep : elaborate each timeline 
		for ($level = 0; $level < count($arr); $level++)
		{
			// get all the times in the current timeline
			$keys = array_keys($arr[$level] ?? []);
			// insert the end working time to evaluate properly the last available time
			$keys[] = $worktime[$level]->endts;

			for ($i = 0; $i < count($keys) - 1; $i++)
			{
				$last_index = -1;

				for ($j = $i + 1; $j < count($keys) && $last_index == -1; $j++)
				{
					/**
					 * If index is last or if current time is not available.
					 *
					 * @since 1.6 	Use empty($arr[$level][$keys[$j]]) to avoid "Undefined Index" notices.
					 * 				These notices may be raised when the reservations were stored for certain
					 * 				times that don't exist anymore.
					 */
					// if ($keys[$j] == count($keys) -1 || $arr[$level][$keys[$j]] == 0)
					if ($keys[$j] == count($keys) -1 || empty($arr[$level][$keys[$j]]))
					{
						// store last index found and stop for statement
						$last_index = $j;
					}
				}

				// if subtraction of last index found with current index is not enough
				if ($keys[$last_index] - $keys[$i] < $n_step)
				{
					// if current time is still available
					if ($arr[$level][$keys[$i]] == 1)
					{
						// mark current time as no more available
						$arr[$level][$keys[$i]] = 2;
					}
				}
			}
		}
		
		$timeline = array();

		foreach ($arr as $a)
		{
			foreach ($a as $hour => $val)
			{
				$timeline[$hour] = $val;
			}
		}
		
		return $timeline;
	}
}
