<?php
/**
 * $Id: class.schedule.php 4446 2009-11-06 20:39:08Z akrivko $
 */
if(!defined('BOOT')) die('Restricted access.');

/**
 * Класс предназначенный для расчетов рабочих часов, времени работы и др.
 *
 */
class Schedule
{
	public static $timetable = array(
		'everyDay' => 'every day (Mon-Fri)',
		'sMonday' => 'start Monday (Mon,Wed,Fri)',
		'sTuesday' => 'start Tuesday (Tue,Thu,Sat)',
	);

	public static function getCompanySchedule($format = Date::FORMAT_TIME_SHORT, $timezoneIdentifier = 'UTC', $customFormat = 'H:i:s', $type = '')
	{
		$return = array();
		$statrWorkTime = ($type == 'support')? Variable::get('SUPPORT_SCHEDULE_START_TIME'): Variable::get('SCHEDULE_START_TIME');
		$endWorkTime = ($type == 'support')? Variable::get('SUPPORT_SCHEDULE_FINISH_TIME'): Variable::get('SCHEDULE_FINISH_TIME');
		$scheduleTimeZone = Variable::get('SCHEDULE_TIMEZONE_IDENTIFIER');
		if(empty($scheduleTimeZone))
		{
			$scheduleTimeZone = Date::getDefaultTimezone();
		}
		if(!empty($statrWorkTime))
		{
			$return['startTime'] = Date::convert($statrWorkTime, $format, $scheduleTimeZone, $timezoneIdentifier, $customFormat);
		}
		if(!empty($endWorkTime))
		{
			$return['endTime'] = Date::convert($endWorkTime, $format, $scheduleTimeZone, $timezoneIdentifier, $customFormat);
		}
		return $return;
	}

	/**
	 * Определяет выходной день или нет.
	 *
	 * @param mixed $date
	 * @return boolean
	 */
	public static function isHoliday($value)
	{
		$scheduleHolidays = Variable::get('SCHEDULE_HOLIDAYS');
		$scheduleTimeZone = Variable::get('SCHEDULE_TIMEZONE_IDENTIFIER');
		if(!(empty($scheduleHolidays) || empty($scheduleTimeZone)))
		{
			$dayOfWeek = Date::format($value, Date::FORMAT_CUSTOM, 'UTC', 'w');
			$dayMonth = Date::format($value, Date::FORMAT_CUSTOM, 'UTC', 'j-n');
			$date = Date::format($value, Date::FORMAT_CUSTOM, 'UTC', 'j-n-Y');
			$holidays = explode(';', $scheduleHolidays);
			foreach($holidays as $holiday)
			{
				$parts = explode('-', $holiday);
				switch(count($parts))
				{
					case 1:
						$result = $dayOfWeek == $holiday;
						break;
					case 2:
						$result = $dayMonth == $holiday;
						break;
					case 3:
						$result = $date == $holiday;
						break;
					default:
						$result = false;
				}
				if($result)
				{
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Высчитывает последний день, учитывая рабочии, по количеству дней
	 *
	 * @param integer $afterDays количество дней
	 * @return string
	 */
	public static function getCompanyNearestWorkingDate($afterDays, $format = Date::FORMAT_MYSQL_DATETIME, $timezoneIdentifier = 'UTC', $customFormat = 'j M Y')
	{
		$companyScheduleHours = self::getCompanySchedule(Date::FORMAT_CUSTOM, $timezoneIdentifier, 'G');
		if(empty($companyScheduleHours))
		{
			return null;
		}
		else
		{
			$nowTimestamp = Date::now(Date::FORMAT_TIMESTAMP, $timezoneIdentifier);
			$timestamp = Date::roundDate($nowTimestamp, 3600, Date::FORMAT_TIMESTAMP, $timezoneIdentifier);
			$nowDateHour = Date::format($timestamp, Date::FORMAT_CUSTOM, $timezoneIdentifier, 'G');
			$isHoliday = self::isHoliday($timestamp);
			if($nowDateHour <= $companyScheduleHours['startTime'] || $nowDateHour > $companyScheduleHours['endTime'] || $isHoliday)
			{
				if($nowDateHour <= $companyScheduleHours['startTime'] && !$isHoliday && $afterDays > 0)
				{
					$afterDays--;
				}
				$timestamp = Date::calculate($companyScheduleHours['endTime'] . ':00', $timestamp, Date::FORMAT_TIMESTAMP, $timezoneIdentifier);
			}
			$resultDate = Date::format($timestamp, $format, $timezoneIdentifier, $customFormat);
			$i = 0;
			while($i < $afterDays)
			{
				$resultDate = Date::calculate('+1 day', $resultDate, $format, $timezoneIdentifier, $customFormat);
				if(self::isHoliday($resultDate))
				{
					$afterDays++;
				}
				$i++;
			}
		}
		return $resultDate;
	}

	public function getSupportShedule()
	{
		$shedule = array(
			'default' => array(
				'working_hours' => array(
					'from' => '11:00',
					'to' => '20:00'
				)
			),
			'01-01' => array(),
			'01-02' => array(),
			'03-08' => array(),
			'05-09' => array(),
			'08-24' => array(),
			'12-31' => array()
		);
		return $shedule;
	}

	public function getShedule()
	{
		$shedule = array(
			'default' => array(
				'working_hours' => array(
					'from' => '10:00',
					'to' => '19:00'
				),
				'lunch_time' => array(
					'from' => '14:30',
					'to' => '15:30'
				)
			),
			'01-01' => array(),
			'01-02' => array(),
			'03-08' => array(),
			'05-09' => array(),
			'08-24' => array(),
			'12-31' => array()
		);
		// BOF: расширяем планировщик, если праздник попал на выходной день, то пн тоже выходной, vstasyuk
		$y = date("Y");
		foreach($shedule as $k => $v)
		{
			if($k != 'default')
			{
				list($m, $d) = explode('-', $k);
				$w = date('w', mktime(0, 0, 0, $m, $d, $y));
				if(in_array($w, array(
					0,
					6
				)))
				{
					if($w == 0)
					{
						$d += 1;
					}
					elseif($w == 6)
					{
						$d += 2;
					}
					list($m, $d) = explode('-', date("m-d", mktime(0, 0, 0, $m, $d, $y)));
					$shedule[$m . '-' . $d] = array();
				}
			}
		}
		// EOF: расширяем планировщик, если праздник попал на выходной день, то пн тоже выходной, vstasyuk
		return $shedule;
	}

	public function getWorkingTime($first_date, $last_date, $shedule)
	{
		$working_days = 0;
		$working_seconds = 0;
		if($first_date && $last_date)
		{
			$first_date_timestamp = strtotime($first_date);
			$last_date_timestamp = strtotime($last_date);
			if($first_date_timestamp !== -1 && $last_date_timestamp !== -1)
			{
				$f_d = getdate($first_date_timestamp);
				$l_d = getdate($last_date_timestamp);
				$f_timestamp = mktime(0, 0, 0, $f_d['mon'], $f_d['mday'], $f_d['year']);
				$l_timestamp = mktime(23, 59, 59, $l_d['mon'], $l_d['mday'], $l_d['year']);
				while($f_timestamp < $l_timestamp)
				{
					$working_seconds_from = '';
					$working_seconds_to = '';
					$lunch_time_from = '';
					$lunch_time_to = '';
					$working_seconds_from_timestamp = 0;
					$working_seconds_to_timestamp = 0;
					$lunch_time_from_timestamp = 0;
					$lunch_time_to_timestamp = 0;
					$day_working_seconds = 0;
					$f_date = date('Y-m-d', $f_timestamp);
					$f_date_short = date('m-d', $f_timestamp);
					$formats = array(
						$f_date,
						$f_date_short,
						'default'
					);
					foreach($formats as $format)
					{
						if(array_key_exists($format, $shedule))
						{
							if(!empty($shedule[$format]['working_hours']))
							{
								$working_seconds_from = $f_date . ' ' . $shedule[$format]['working_hours']['from'];
								$working_seconds_to = $f_date . ' ' . $shedule[$format]['working_hours']['to'];
								$working_seconds_from_timestamp = strtotime($working_seconds_from);
								$working_seconds_to_timestamp = strtotime($working_seconds_to);
							}
							if(!empty($shedule[$format]['lunch_time']))
							{
								$lunch_time_from = $f_date . ' ' . $shedule[$format]['lunch_time']['from'];
								$lunch_time_to = $f_date . ' ' . $shedule[$format]['lunch_time']['to'];
								$lunch_time_from_timestamp = strtotime($lunch_time_from);
								$lunch_time_to_timestamp = strtotime($lunch_time_to);
							}
							break;
						}
					}
					$next_day_timestamp = strtotime('+1 day', $f_timestamp);
					$day_working_seconds = get_date_diff($working_seconds_from_timestamp, $working_seconds_to_timestamp) - get_date_diff($lunch_time_from_timestamp, $lunch_time_to_timestamp);
					if($f_timestamp < $first_date_timestamp || $next_day_timestamp >= $l_timestamp)
					{
						if($f_timestamp < $first_date_timestamp)
						{
							if($first_date_timestamp > $working_seconds_from_timestamp && $first_date_timestamp < $lunch_time_from_timestamp)
							{
								$day_working_seconds = $day_working_seconds - get_date_diff($working_seconds_from_timestamp, $first_date_timestamp);
							}
							elseif($first_date_timestamp >= $lunch_time_from_timestamp && $first_date_timestamp < $lunch_time_to_timestamp)
							{
								$day_working_seconds = get_date_diff($lunch_time_to_timestamp, $working_seconds_to_timestamp);
							}
							elseif($first_date_timestamp >= $lunch_time_to_timestamp && $first_date_timestamp <= $working_seconds_to_timestamp)
							{
								//							$day_working_seconds = get_date_diff($first_date_timestamp, $working_seconds_to_timestamp);
								if($first_date_timestamp > $working_seconds_from_timestamp)
								{
									$day_working_seconds = get_date_diff($first_date_timestamp, $working_seconds_to_timestamp);
								}
								else
								{
									$day_working_seconds = get_date_diff($working_seconds_from_timestamp, $working_seconds_to_timestamp) - get_date_diff($lunch_time_from_timestamp, $lunch_time_to_timestamp);
								}
							}
							elseif($first_date_timestamp > $working_seconds_to_timestamp)
							{
								$day_working_seconds = 0;
							}
						}
						if($next_day_timestamp >= $l_timestamp)
						{
							if($last_date_timestamp <= $working_seconds_from_timestamp)
							{
								$day_working_seconds = 0;
							}
							elseif($last_date_timestamp > $working_seconds_from_timestamp && $last_date_timestamp <= $lunch_time_from_timestamp)
							{
								if($f_timestamp < $first_date_timestamp && $first_date_timestamp > $working_seconds_from_timestamp)
								{
									$day_working_seconds = get_date_diff($first_date_timestamp, $last_date_timestamp);
									//клиент написал в рабочее времени менеджера, менеджер ответил до перерыва
								}
								elseif($f_timestamp < $first_date_timestamp && $first_date_timestamp < $working_seconds_from_timestamp)
								{
									$day_working_seconds = get_date_diff($working_seconds_from_timestamp, $last_date_timestamp);
									//клиент написал до начала  рабочего времени менеджера, менеджер ответил до перерыва
								}
								else
								{
									$day_working_seconds = get_date_diff($working_seconds_from_timestamp, $last_date_timestamp);
									//клиент написал после рабочего времени менеджера, менеджер ответил до перерыва на сл день
								}
							}
							elseif($last_date_timestamp > $lunch_time_from_timestamp && $last_date_timestamp < $lunch_time_to_timestamp)
							{
								if($f_timestamp < $first_date_timestamp && $first_date_timestamp < $working_seconds_from_timestamp)
								{
									$day_working_seconds = get_date_diff($first_date_timestamp, $lunch_time_from_timestamp) - get_date_diff($first_date_timestamp, $working_seconds_from_timestamp);
									//клиент написал до рабочего временя менеджера, менеджер ответил в перерыв
								}
								elseif($f_timestamp < $first_date_timestamp && $first_date_timestamp > $working_seconds_from_timestamp && !($first_date_timestamp >= $lunch_time_from_timestamp && $first_date_timestamp <= $lunch_time_to_timestamp && $last_date_timestamp >= $lunch_time_from_timestamp && $last_date_timestamp <= $lunch_time_to_timestamp))
								{
									$day_working_seconds = get_date_diff($first_date_timestamp, $lunch_time_from_timestamp);
									//клиент написал в рабочее время менеджера, менеджер ответил в перерыв
								}
								elseif($f_timestamp < $first_date_timestamp && $first_date_timestamp >= $lunch_time_from_timestamp && $first_date_timestamp <= $lunch_time_to_timestamp && $last_date_timestamp >= $lunch_time_from_timestamp && $last_date_timestamp <= $lunch_time_to_timestamp)
								{
									if($first_date_timestamp >= $lunch_time_from_timestamp && $first_date_timestamp <= $lunch_time_to_timestamp && $last_date_timestamp >= $lunch_time_from_timestamp && $last_date_timestamp <= $lunch_time_to_timestamp)
									{
										$day_working_seconds = get_date_diff($first_date_timestamp, $last_date_timestamp);
										//клиент написал в перерыв менеджера, менеджер ответил в перерыв
									}
								}
								else
								{
									$day_working_seconds = get_date_diff($working_seconds_from_timestamp, $lunch_time_from_timestamp);
									//клиент написал после рабочего времени менеджера, менеджер ответил в перерыв на сл день
								}
							}
							elseif($last_date_timestamp >= $lunch_time_to_timestamp && $last_date_timestamp <= $working_seconds_to_timestamp)
							{
								$day_working_seconds = $day_working_seconds - get_date_diff($last_date_timestamp, $working_seconds_to_timestamp);
							}
						}
					}
					if(!in_array(date('w', $f_timestamp), array(
						0,
						6
					)) || $format != 'default')
					{
						if($day_working_seconds)
						{
							$working_days++;
							$working_seconds += $day_working_seconds;
						}
					}
					$f_timestamp = $next_day_timestamp;
				}
			}
		}
		$working_minutes = $working_seconds / 60;
		$working_hours = $working_seconds / 3600;
		return array(
			'days' => $working_days,
			'hours' => $working_hours,
			'minutes' => $working_minutes,
			'seconds' => $working_seconds
		);
	}

	/**
	 * Подсчитывается количество часов, которые попали в заданый интервал, результат выводится в секундах
	 * @param array() $dateArray ('time_start', 'time_finish', 'dinner_start', 'dinner_finish')
	 * @param string $firstInterval
	 * @param string $lastInterval
	 * @return integer Секунды
	 */
	public function getHoursFromInterval($dateArray, $firstInterval, $lastInterval, $useStartTime = false, $useDinner = true)
	{
		$firstInterval = Date::getTimeFromDayStart($firstInterval);
		$lastInterval = Date::getTimeFromDayStart($lastInterval);
		$timeStart = Date::getTimeFromDayStart($dateArray['time_start']);
		$timeFinish = Date::getTimeFromDayStart($dateArray['time_finish']);
		$dinnerStart = Date::getTimeFromDayStart($dateArray['dinner_start']);
		$dinnerFinish = Date::getTimeFromDayStart($dateArray['dinner_finish']);

		$dateDiff = 0;
		if($firstInterval > $lastInterval)
		{
			$firstInterval -= 24 * 60 * 60;
		}
		if($timeStart > $timeFinish)
		{
			$timeStart -= 24 * 60 * 60;
		}
		if($dinnerStart > $dinnerFinish)
		{
			$dinnerStart -= 24 * 60 * 60;
		}

		if($useStartTime)
		{
			if($firstInterval <= $timeStart && $timeStart <= $lastInterval)
			{
				;
			}
			elseif($firstInterval <= ($timeStart - 24 * 3600) && ($timeStart - 24 * 3600) <= $lastInterval)
			{
				$timeStart -= 24 * 3600;
				$timeFinish -= 24 * 3600;
			}
			else
			{
				return $dateDiff;
			}
		}

		if($firstInterval < 0 && $timeStart >= 0 && $timeFinish >= 24*3600 + $firstInterval)
		{
			$firstInterval += 24*3600;
			$lastInterval += 24*3600;
		}

		$startTime = max($firstInterval, $timeStart);
		$finishTime = min($lastInterval, $timeFinish);
		$dateDiff = max($finishTime - $startTime, 0);
		if($useDinner && $startTime < $dinnerStart && $dinnerStart < $finishTime && $startTime < $dinnerFinish && $dinnerFinish < $finishTime)
		{
			$startDinner = max($firstInterval, $dinnerStart);
			$finishDinner = min($lastInterval, $dinnerFinish);
			$dateDiffDinner = max($finishDinner - $startDinner, 0);
			$dateDiff -= $dateDiffDinner;
		}
		return $dateDiff;
	}

	public function getDateDiff($first_date, $last_date)
	{
		/*
		@todo Можно было бы написать вот так - проще, проверить
		$firstInterval = Date::getTimeFromDayStart($first_date);
		$lastInterval = Date::getTimeFromDayStart($last_date);
		$result = $lastInterval - $firstInterval;
		if($result < 0)
		{
			$result += 86400;
		}
		*/
		$result = false;
		if($first_date && $last_date)
		{
			if(is_numeric($first_date) && is_numeric($last_date))
			{
				$result = $last_date - $first_date;
			}
			else
			{
				$first_date_timestamp = strtotime($first_date);
				$last_date_timestamp = strtotime($last_date);
				if($first_date_timestamp !== -1 && $last_date_timestamp !== -1)
				{
					$result = $last_date_timestamp - $first_date_timestamp;
				}
			}
			// если время со следующего дня, например '20:00:00' => '03:00:00'
			if($result < 0)
			{
				$result += 86400;
			}
		}
		if($result < 0)
		{
			$result = false;
		}
		return $result;
	}

	/**
	 * Формирует массив, планировщик времени работы
	 *
	 * @param integer $y
	 * @param integer $m
	 * @return array
	 */
	public function getSchedule($department, $period, $y = 0, $m = 0, $d = 0, $userIds = array())
	{
		if(!empty($userIds) && !is_array($userIds))
		{
			$userIds = array(
				$userIds
			);
		}
		$y = (int)$y;
		$m = (int)$m;
		$d = (int)$d;
		//сколько дней в месяце
		$monthdaysCnt = date('j', mktime(0, 0, 0, $m + 1, 0, $y));
		$scheduleDays = (int)$period;
		if($scheduleDays > $monthdaysCnt)
		{
			$scheduleDays = $monthdaysCnt;
		}
		// масив из дней
		$monthdays = array();
		for($i = 0; $i < $scheduleDays; $i++)
		{
			$mktime = mktime(0, 0, 0, $m, $i + $d, $y);
			$monthdays[date('Y-m-d', $mktime)] = array(
				'd' => date('D', $mktime)
			);
		}
		if($i + $d > $monthdaysCnt)
		{
			$m = array(
				$m,
				date('n', $mktime)
			);
			$y = array(
				$y,
				date('Y', $mktime)
			);
		}
		//матрица из пользователей и дней
		$schedule = array();
		$userClass = new User();
		if(!empty($userIds))
		{
			$listUser = array();
			foreach($userIds as $userId)
			{
				$tmp = $userClass->getUsersByHierarchy($userId, false, null, $department, true);
				$listUser += $userClass->getUserPairListFromData($tmp);
				unset($tmp);
			}
		}
		else
		{
			$filter = array(
				'`enabled` = ?' => 'yes',
				'`department` = ?' => $department
			);
			$order = array(
				'email' => 'ASC'
			);
			$listUser = $userClass->getPairUserList('dev', true, 'id', '@email', $filter, $order);
		}
		if(!empty($listUser))
		{
			foreach($listUser as $key => $val)
			{
				$id = $key;
				$schedule[$id]['email'] = $val;
				$schedule[$id]['schedule'] = $monthdays;
			}
			//fill
			$dbScheduleDate = new DbScheduleDate();
			$dbScheduleTime = new DbScheduleTime();
			$sql = "
				SELECT
					*,
					DAYOFMONTH(`sd`.`schedule_date`) `dayofmonth`
				FROM
					{" . $dbScheduleDate->getTable() . "} `sd`,
					{" . $dbScheduleTime->getTable() . "} `st`
				WHERE
					`sd`.`schedule_time_id` = `st`.`id`
			";
			$usersList = array_keys($listUser);
			$filter = array(
				'`sd`.`user_id` IN (?)' => $usersList,
				'MONTH(`sd`.`schedule_date`) IN(?)' => $m,
				'YEAR(`sd`.`schedule_date`) IN(?)' => $y
			);
			$dbClass = Db::getInstance();
			$scheduleRecords = $dbClass->getRecords($sql, array(), $filter);
			foreach($scheduleRecords as $record)
			{
				$id = $record['user_id'];
				$schedule[$id]['schedule'][$record['schedule_date']]['info'] = $record;
			}
			// attendances
			$filter = array(
				'`user_id` IN (?)' => $usersList,
				'MONTH(`date`) IN(?)' => $m,
				'YEAR(`date`) IN(?)' => $y
			);
			$dbReportAttendance = new DbReportAttendance();
			$attendances = $dbClass->getTableRecords($dbReportAttendance->getTable(), $filter);
			foreach($attendances as $record)
			{
				$id = $record['user_id'];
				$schedule[$id]['schedule'][$record['date']]['attendances'] = $record;
			}
		}
		return $schedule;
	}

	public function hasScheduleForPeriodByUserIds($fromDate, $toDate, $userIds)
	{
		$return = array();
		if(empty($userIds))
		{
			return $return;
		}
		$dbScheduleDate = new DbScheduleDate();
		$sql = "
			SELECT
				`schedule_date`,
				`user_id`
			FROM
				{" . $dbScheduleDate->getTable() . "}
		";
		$filter = array(
			'`schedule_time_id` > ?' => 0,
			'`user_id` IN (?)' => $userIds,
			'`schedule_date` >= ?' => $fromDate,
			'`schedule_date` <= ?' => $toDate
		);
		$dbClass = Db::getInstance();
		$scheduleRecords = $dbClass->getRecords($sql, array(), $filter);
		foreach($scheduleRecords as $record)
		{
			$return[$record['user_id']][$record['schedule_date']] = true;
		}
		return $return;
	}

	public function isUsersAtWork($nowDate, $userIds)
	{
		if(empty($userIds))
		{
			return array();
		}
		$return = array();
		$dbScheduleDate = new DbScheduleDate();
		$dbScheduleTime = new DbScheduleTime();
		$sql = "
			SELECT
				`schedule_date`,
				`user_id`
			FROM
				{" . $dbScheduleDate->getTable() . "} sd
		";
		$filter = array(
			'`sd`.`schedule_time_id` > ?' => 0,
			'`sd`.`user_id` IN (?)' => $userIds,
			'`sd`.`schedule_date` >= ?' => $nowDate,
			'`sd`.`schedule_date` <= ?' => $nowDate
		);
		$dbClass = Db::getInstance();
		$scheduleRecords = $dbClass->getRecords($sql, array(), $filter);
		foreach($userIds as $user)
		{
			$return[$user] = false;
			foreach($scheduleRecords as $record)
			{
				if($record['user_id'] == $user)
				{
					$return[$user] = true;
				}
			}
		}
		return $return;
	}

	/**
	 * выбирает из бд промежутки рабочего времени
	 *
	 * @param string $department
	 * @return array
	 */
	public function getScheduleTime($department)
	{
		$dbScheduleTime = new DbScheduleTime();
		$scheduleTime = array();
		$sql = "
			SELECT
				*,
				TIME_FORMAT(`st`.`time_start`, '%H:%i') `time_start`,
				TIME_FORMAT(`st`.`time_finish`, '%H:%i') `time_finish`
			FROM
				{" . $dbScheduleTime->getTable() . "} `st`
		";
		$filter = array(
			'`department` =?' => $department
		);
		$order = array(
			'`st`.`time_start`' => 'ASC'
		);
		$dbClass = Db::getInstance();
		$scheduleTime = $dbClass->getRecords($sql, array(), $filter, $order, 0, 0, 'id');
		return $scheduleTime;
	}

	public function addScheduleTime($timeStart, $timeFinish, $hours, $color, $dinnerStart, $dinnerFinish, $department)
	{
		$dbClass = Db::getInstance();
		$dbScheduleTime = new DbScheduleTime();
		$records = array(
			'`time_start` = ?' => $timeStart,
			'`time_finish` = ?' => $timeFinish,
			'`dinner_start` = ?' => $dinnerStart,
			'`dinner_finish` = ?' => $dinnerFinish,
			'`hours` = ?' => $hours,
			'`color` = ?' => $color,
			'`department` = ?' => $department
		);
		$duplicates = array(
			'`hours` = ?' => $hours,
			'`color` = ?' => $color
		);
		$res = $dbClass->insert($dbScheduleTime->getTable(), $records, $duplicates);
		return $res;
	}

	public function editScheduleTime($timeStart, $timeFinish, $hours, $color, $dinnerStart, $dinnerFinish)
	{
		$dbClass = Db::getInstance();
		$dbScheduleTime = new DbScheduleTime();
		$result = true;
		foreach($timeStart as $k => $v)
		{
			if(!empty($timeStart[$k]) && strtotime($timeStart[$k]) && !empty($timeFinish[$k]) && strtotime($timeFinish[$k]) && !empty($hours[$k]) && is_numeric($hours[$k]) && !empty($color[$k]) && $color[$k][0] == '#' && isset($color[$k][1]))
			{
				$time_start2 = ($timeStart[$k]);
				$time_finish2 = ($timeFinish[$k]);
				$dinner_start2 = ($dinnerStart[$k]);
				$dinner_finish2 = ($dinnerFinish[$k]);
				$hours2 = ($hours[$k]);
				$color2 = (substr($color[$k], 1));
				$record = array(
					'`time_start` = ?' => $time_start2,
					'`time_finish` = ?' => $time_finish2,
					'`dinner_start` = ?' => $dinner_start2,
					'`dinner_finish` = ?' => $dinner_finish2,
					'`hours` = ?' => $hours2,
					'`color` = ?' => $color2
				);
				$filter = array(
					'`id` = ?' => $k
				);
				$res = $dbClass->update($dbScheduleTime->getTable(), $record, $filter);
				$result &= $res;
			}
			else
			{
				Logger::add('not correct data', Logger::TYPE_ERROR, null, __FILE__, __LINE__);
			}
		}
		return $result;
	}

	/**
	 * Удаление временного интервала
	 *
	 * @param array $ids
	 * @param mixed $department
	 * @return mixed
	 */
	public function deleteScheduleTime($ids, $department)
	{
		$dbClass = Db::getInstance();
		$dbScheduleTime = new DbScheduleTime();
		$dbScheduleDate = new DbScheduleDate();
		$filter = array(
			'`schedule_time_id` IN (?)' => $ids
		);
		$result = $dbClass->getRecordCount($dbScheduleDate->getTable(), $filter);
		if($result === 0)
		{
			$filter = array(
				'`id` IN (?)' => $ids,
				'`department` IN (?)' => $department
			);
			$res = $dbClass->delete($dbScheduleTime->getTable(), $filter);
			return $res;
		}
		return false;
	}

	/**
	 * Добавляем рассписание на определенный период
	 *
	 * @param string $date
	 * @param integer $period
	 * @param integer $userId
	 * @param integer $scheduleTimeId
	 * @return boolean были ошибки или нет
	 */
	public function addSchedulePeriods($date, $period, $userId, $scheduleTimeId, $timetable)
	{
		list($year, $month, $day) = explode('-', $date);
		$days = Date::getDaysByDate($date, $period);
		$days = $this->getTimetable($days, $timetable);
		$result = true;
		foreach($days as $k => $day)
		{
			list($year, $month, $d) = explode('-', $k);
			$res = $this->updateScheduleDate($year . '-' . $month . '-' . sprintf('%02d', $d), $userId, $scheduleTimeId);
			$result &= (boolean)$res;
		}
		return $result;
	}

	/**
	 * Добавляем рассписание на целый месяц
	 *
	 * @param string $date
	 * @param integer $userId
	 * @param integer $scheduleTimeId
	 * @return boolean были ошибки или нет
	 */
	public function addScheduleMounth($date, $userId, $scheduleTimeId, $timetable)
	{
		list($year, $month) = explode('-', $date);
		$days = Date::getDaysByMonth($date);
		$days = $this->getTimetable($days, $timetable);
		$result = true;
		foreach($days as $k => $day)
		{
			$res = $this->updateScheduleDate($year . '-' . $month . '-' . sprintf('%02d', $k), $userId, $scheduleTimeId);
			$result &= (boolean)$res;
		}
		return $result;
	}

	private function getTimetable($days, $timetable)
	{
		$daysTmp = array();
		foreach($days as $k => $day)
		{
			switch($timetable)
			{
				case 'sMonday':
					if(in_array($day, array('Mon','Wed','Fri')))
					{
						$daysTmp[$k] = $day;
					}
					break;
				case 'sTuesday':
					if(in_array($day, array('Tue','Thu','Sat')))
					{
						$daysTmp[$k] = $day;
					}
					break;
				case 'everyDay':
				default:
					if(!in_array($day, array(
						'Sat',
						'Sun'
					)))
					{
						$daysTmp[$k] = $day;
					}
			}
		}
		return $daysTmp;
	}

	public function deleteScheduleDate($date, $userId)
	{
		if(strtotime($date))
		{
			$dbClass = Db::getInstance();
			$dbScheduleDate = new DbScheduleDate();
			$filter = array(
				'`schedule_date` = ?' => $date,
				'`user_id` = ?' => $userId
			);
			$res = $dbClass->delete($dbScheduleDate->getTable(), $filter);
			return $res;
		}
		return false;
	}

	public function updateScheduleDate($date, $userId, $scheduleTimeId = 0, $overtime = 0, $duty = 0)
	{
		if(strtotime($date))
		{
			$dbClass = Db::getInstance();
			$dbScheduleDate = new DbScheduleDate();
			$userClass = new User();
			$userId = (int)$userId;
			// проверяем, есть ли такой пользователь
			$user = $userClass->getUserById($userId);
			if($user)
			{
				$record = array(
					'`schedule_date` = ?' => $date,
					'`user_id` = ?' => $userId,
					'`overtime` = ?' => $overtime,
					'`duty` = ?' => $duty
				);
				$duplicates = array(
					'`overtime` = ?' => $overtime,
					'`duty` = ?' => $duty
				);
				// проверяем можно ли данному пользователю повесить данное расписание
				$scheduleTime = $this->getScheduleTime($user->department);
				if($scheduleTimeId !== null && ($scheduleTimeId == 0 || isset($scheduleTime[$scheduleTimeId])))
				{
					$record['`schedule_time_id` = ?'] = $scheduleTimeId;
					$duplicates['`schedule_time_id` = ?'] = $scheduleTimeId;
				}
				$res = $dbClass->insert($dbScheduleDate->getTable(), $record, $duplicates);
				return $res;
			}
			else
			{
				Logger::add('no such userId:' . $userId, Logger::TYPE_ERROR, null, __FILE__, __LINE__);
			}
		}
		else
		{
			Logger::add('not correct date:' . $date, Logger::TYPE_ERROR, null, __FILE__, __LINE__);
			return false;
		}
	}

	public function getScheduleWorkingHours($from, $to, $filter = array())
	{
		$dbScheduleDate = new DbScheduleDate();
		$dbScheduleTime = new DbScheduleTime();
		$filterStr = '';
		$params = array(
			$from,
			$to
		);
		if(!empty($filter) && is_array($filter))
		{
			foreach($filter as $expresion => $param)
			{
				$filterStr .= ' AND ' . $expresion;
				$params[] = $param;
			}
		}
		$sql = "
			SELECT
				*,
				`d`.`id`
			FROM
				{" . $dbScheduleDate->getTable() . "} `d`,
				{" . $dbScheduleTime->getTable() . "} `t`
			WHERE
				`d`.`schedule_time_id` = `t`.`id` AND
				`d`.`schedule_date` BETWEEN ? AND ?
				" . $filterStr;
		$dbClass = Db::getInstance();
		$scheduleWorkingHours = $dbClass->getRecords($sql, $params);
		return $scheduleWorkingHours;
	}

	public function isScheduleDateTime($userId, $dateTime)
	{
		$startTimeDay = Variable::get('START_TIME_DAY');
		$finishTimeDay = Variable::get('FINISH_TIME_DAY');
		$from = Date::calculate('-1 day', $dateTime, Date::FORMAT_MYSQL_DATE) . ' ' . $startTimeDay;
		$to = Date::format($dateTime, Date::FORMAT_MYSQL_DATE) . ' ' . $finishTimeDay;
		$filter = array(
			'`user_id` = ?' => $userId
		);
		$scheduleWorkingHours = $this->getScheduleWorkingHours($from, $to, $filter);
		if(!empty($scheduleWorkingHours))
		{
			foreach($scheduleWorkingHours as $v)
			{
				$dateTimeStart = $v['schedule_date'] . ' ' . $v['time_start'];
				$dateTimeFinish = $v['schedule_date'] . ' ' . $v['time_finish'];
				$timeStart = Date::getTimeFromDayStart($v['time_start']);
				$timeFinish = Date::getTimeFromDayStart($v['time_finish']);
				if($timeStart > $timeFinish)
				{
					$dateTimeFinish = Date::calculate('+1 day', $dateTimeFinish, Date::FORMAT_MYSQL_DATETIME);
				}
				$overtime = (float)$v['overtime'];
				if(!empty($overtime))
				{
					$dateTimeFinish = Date::calculate('+' . ($overtime*60) . ' minutes', $dateTimeFinish, Date::FORMAT_MYSQL_DATETIME);
				}
				//@todo возможно нужно будет так же написать, что бы был учет позданий
				// на этот момент, мы знаем точное время начала работы и точное время конца работы с учетом суток (24) и с учетом овертаймов
				if(Date::isLessThan($dateTimeStart, $dateTime) && Date::isLessThan($dateTime, $dateTimeFinish))
				{
					return true;
				}
			}
			return false;
		}
		else
		{
			return false;
		}
	}

	public function getUserLastWorkingDay($userId, $date = '')
	{
		if(empty($date))
		{
			$date = Date::now(Date::FORMAT_MYSQL_DATE);
		}
		$dbScheduleDate = new DbScheduleDate();
		$dbClass = Db::getInstance();
		$query = "
			SELECT
				*
			FROM
				{" . $dbScheduleDate->getTable() . "}
			WHERE
				`user_id` = ".$userId."
				AND `schedule_time_id` > 0
				AND `schedule_date` < '".$date."'
			ORDER BY `schedule_date` DESC
			LIMIT 1
		";
		$lastWorkingDay = $dbClass->getRecords($query);
		return current($lastWorkingDay);
	}

	public function countPerformersPresence($userIds, $time = false)
	{
		if(empty($time))
		{
			$time = Date::now();
		}
		list($d, $t) = explode(' ', $time);

		$dbScheduleDate = new DbScheduleDate();
		$dbScheduleTime = new DbScheduleTime();
		$dbReportAttendance = new DbReportAttendance();
		$dbClass = Db::getInstance();
		$filter = array(
			'`user_id` IN (?)' => $userIds
		);
		$sql = "
			SELECT
				`d`.`user_id`
			FROM
				{" . $dbScheduleDate->getTable() . "} `d`
			INNER JOIN
				{" . $dbScheduleTime->getTable() . "} `t`
			ON
				`t`.`id` = `d`.`schedule_time_id`
			INNER JOIN
				{" . $dbReportAttendance->getTable() . "} `a`
			ON
				`a`.`user_id` = `d`.`user_id`
			WHERE
				(`a`.`type` = 'presence' OR `d`.`overtime` > 0) AND
				`d`.`user_id` IN (?) AND
				`d`.`schedule_date` = ? AND
				? BETWEEN `t`.`time_start` AND `t`.`time_finish`
			GROUP BY
				`d`.`user_id`
		";
		$params = array(
			$userIds,
			$d,
			$t
		);
		$result = $scheduleWorkingHours = $dbClass->getRecords($sql, $params);
		if(!empty($result))
		{
			return count($result);
		}
		return false;
	}

}