<?php
if (!defined("ROOT_PATH"))
{
	header("HTTP/1.1 403 Forbidden");
	exit;
}
class pjAppController extends pjController
{
	public $models = array();

	public $defaultLocale = 'admin_locale_id';
	
	private $layoutRange = array(1, 2);
	
	public $defaultFields = 'fields';
	
	public $defaultFieldsIndex = 'fields_index';
	
	protected function loadSetFields($force=FALSE)
	{
		$registry = pjRegistry::getInstance();
		if ($force
				|| !isset($_SESSION[$this->defaultFieldsIndex])
				|| $_SESSION[$this->defaultFieldsIndex] != $this->option_arr['o_fields_index']
				|| !isset($_SESSION[$this->defaultFields])
				|| empty($_SESSION[$this->defaultFields]))
		{
			pjAppController::setFields($this->getLocaleId());
	
			# Update session
			if ($registry->is('fields'))
			{
				$_SESSION[$this->defaultFields] = $registry->get('fields');
			}
			$_SESSION[$this->defaultFieldsIndex] = $this->option_arr['o_fields_index'];
		}
	
		if (isset($_SESSION[$this->defaultFields]) && !empty($_SESSION[$this->defaultFields]))
		{
			# Load fields from session
			$registry->set('fields', $_SESSION[$this->defaultFields]);
		}
	
		return TRUE;
	}
	
	public function getLayoutRange()
	{
		return $this->layoutRange;
	}
	
	public static function setTimezone($timezone="UTC")
    {
    	if (in_array(version_compare(phpversion(), '5.1.0'), array(0,1)))
		{
			date_default_timezone_set($timezone);
		} else {
			$safe_mode = ini_get('safe_mode');
			if ($safe_mode)
			{
				putenv("TZ=".$timezone);
			}
		}
    }

	public static function setMySQLServerTime($offset="-0:00")
    {
		pjAppModel::factory()->prepare("SET SESSION time_zone = :offset;")->exec(array('offset' => $offset));
    }
    
	public function setTime()
	{
		if (isset($this->option_arr['o_timezone']))
		{
			$offset = $this->option_arr['o_timezone'] / 3600;
			if ($offset > 0)
			{
				$offset = "-".$offset;
			} elseif ($offset < 0) {
				$offset = "+".abs($offset);
			} elseif ($offset === 0) {
				$offset = "+0";
			}
	
			pjAppController::setTimezone('Etc/GMT' . $offset);
			if (strpos($offset, '-') !== false)
			{
				$offset = str_replace('-', '+', $offset);
			} elseif (strpos($offset, '+') !== false) {
				$offset = str_replace('+', '-', $offset);
			}
			pjAppController::setMySQLServerTime($offset . ":00");
		}
	}
    
    public function beforeFilter()
    {
    	$this->appendJs('jquery.min.js', PJ_THIRD_PARTY_PATH . 'jquery/');
    	$dm = new pjDependencyManager(PJ_THIRD_PARTY_PATH);
    	$dm->load(PJ_CONFIG_PATH . 'dependencies.php')->resolve();
    	$this->appendJs('jquery-migrate.min.js', $dm->getPath('jquery_migrate'), FALSE, FALSE);
    	$this->appendJs('pjAdminCore.js');
    	$this->appendCss('reset.css');
    		
    	$this->appendJs('js/jquery-ui.custom.min.js', PJ_THIRD_PARTY_PATH . 'jquery_ui/');
    	$this->appendCss('css/smoothness/jquery-ui.min.css', PJ_THIRD_PARTY_PATH . 'jquery_ui/');
    		
    	$this->appendCss('pj-all.css', PJ_FRAMEWORK_LIBS_PATH . 'pj/css/');
    	$this->appendCss('admin.css');
		
    	if ($_GET['controller'] != 'pjInstaller')
		{
		    if(!$this->isSuperAdmin() && in_array($_GET['controller'], array('pjLocale', 'pjCountry', 'pjBackup')))
		    {
		        pjUtil::redirect($_SERVER['PHP_SELF'] . "?controller=pjAdminOptions&action=pjActionIndex");
		    }
		    if(!$this->isSuperAdmin() && in_array($_GET['controller'], array('pjSms')) && in_array($_GET['action'], array('pjActionIndex')))
		    {
		        pjUtil::redirect($_SERVER['PHP_SELF'] . "?controller=pjAdminOptions&action=pjActionIndex");
		    }
			$this->models['Option'] = pjOptionModel::factory();
			$this->option_arr = $this->models['Option']->getPairs($this->getForeignId());
			$this->set('option_arr', $this->option_arr);
			$this->setTime();
			
			if (!isset($_SESSION[$this->defaultLocale]))
			{
				$locale_arr = pjLocaleModel::factory()->where('is_default', 1)->limit(1)->findAll()->getData();
				if (count($locale_arr) === 1)
				{
					$this->setLocaleId($locale_arr[0]['id']);
				}
			}
			$this->loadSetFields(true);
		}
    }
    
    public function getForeignId()
    {
    	return 1;
    }
    
    public function isAdmin()
    {
        return (int) $this->getRoleId() === 1 || (int) $this->getRoleId() === 4;
    }
    
    public function isEmployee()
    {
    	return (int) $this->getRoleId() === 2;
    }

	public function isTutor()
	{
		return (int) $this->getRoleId() === 3;
	}
	
	public function isSuperAdmin()
	{
	    return (int) $this->getRoleId() === 4;
	}
    
	public function isInvoiceReady()
	{
		return $this->isAdmin();
	}
    
    public function isCountryReady()
    {
    	return $this->isAdmin();
    }
    
    public function isOneAdminReady()
    {
    	return $this->isAdmin();
    }
    
    public static function setFields($locale)
    {
    	if(isset($_SESSION['lang_show_id']) && (int) $_SESSION['lang_show_id'] == 1)
		{
			$fields = pjMultiLangModel::factory()
				->select('CONCAT(t1.content, CONCAT(":", t2.id, ":")) AS content, t2.key')
				->join('pjField', "t2.id=t1.foreign_id", 'inner')
				->where('t1.locale', $locale)
				->where('t1.model', 'pjField')
				->where('t1.field', 'title')
				->findAll()
				->getDataPair('key', 'content');
		}else{
			$fields = pjMultiLangModel::factory()
				->select('t1.content, t2.key')
				->join('pjField', "t2.id=t1.foreign_id", 'inner')
				->where('t1.locale', $locale)
				->where('t1.model', 'pjField')
				->where('t1.field', 'title')
				->findAll()
				->getDataPair('key', 'content');
		}
		$registry = pjRegistry::getInstance();
		$tmp = array();
		if ($registry->is('fields'))
		{
			$tmp = $registry->get('fields');
		}
		$arrays = array();
		foreach ($fields as $key => $value)
		{
			if (strpos($key, '_ARRAY_') !== false)
			{
				list($prefix, $suffix) = explode("_ARRAY_", $key);
				if (!isset($arrays[$prefix]))
				{
					$arrays[$prefix] = array();
				}
				$arrays[$prefix][$suffix] = $value;
			}
		}
		require PJ_CONFIG_PATH . 'settings.inc.php';
		$fields = array_merge($tmp, $fields, $settings, $arrays);
		$registry->set('fields', $fields);
    }

    public static function jsonDecode($str)
	{
		$Services_JSON = new pjServices_JSON();
		return $Services_JSON->decode($str);
	}
	
	public static function jsonEncode($arr)
	{
		$Services_JSON = new pjServices_JSON();
		return $Services_JSON->encode($arr);
	}
	
	public static function jsonResponse($arr)
	{
		header("Content-Type: application/json; charset=utf-8");
		echo pjAppController::jsonEncode($arr);
		exit;
	}

	public function getLocaleId()
	{
		return isset($_SESSION[$this->defaultLocale]) && (int) $_SESSION[$this->defaultLocale] > 0 ? (int) $_SESSION[$this->defaultLocale] : 1;
	}
	
	public function setLocaleId($locale_id)
	{
		$_SESSION[$this->defaultLocale] = (int) $locale_id;
	}
	
	public static function friendlyURL($str, $divider='-')
	{
		$str = pjMultibyte::strtolower($str);
		$str = trim($str); //  trim leading and trailing spaces
		$str = preg_replace('/[_|\s]+/', $divider, $str); //  change all spaces and underscores to a hyphen
		$str = preg_replace('/\x{00C5}/u', 'AA', $str);
		$str = preg_replace('/\x{00C6}/u', 'AE', $str);
		$str = preg_replace('/\x{00D8}/u', 'OE', $str);
		$str = preg_replace('/\x{00E5}/u', 'aa', $str);
		$str = preg_replace('/\x{00E6}/u', 'ae', $str);
		$str = preg_replace('/\x{00F8}/u', 'oe', $str);
		$str = preg_replace('/[^a-z\x{0400}-\x{04FF}0-9-]+/u', '', $str); //  remove all non-cyrillic, non-numeric characters except the hyphen
		$str = preg_replace('/[-]+/', $divider, $str); //  replace multiple instances of the hyphen with a single instance
		$str = preg_replace('/^-+|-+$/', '', $str); //  trim leading and trailing hyphens
		return $str;
	}
	
	public function pjActionCheckInstall()
	{
		$this->setLayout('pjActionEmpty');
	
		$result = array('status' => 'OK', 'code' => 200, 'text' => 'Operation succeeded', 'info' => array());
		$folders = array(
				'app/web/upload',
				'app/web/upload/services'
		);
		foreach ($folders as $dir)
		{
			if (!is_writable($dir))
			{
				$result['status'] = 'ERR';
				$result['code'] = 101;
				$result['text'] = 'Permission requirement';
				$result['info'][] = sprintf('Folder \'<span class="bold">%1$s</span>\' is not writable. You need to set write permissions (chmod 777) to directory located at \'<span class="bold">%1$s</span>\'', $dir);
			}
		}
	
		return $result;
	}
	
	public function pjActionAfterInstall()
	{
		pjInvoiceConfigModel::factory()->set('id', 1)->modify(array(
			'o_booking_url' => "index.php?controller=pjAdminBookings&action=pjActionUpdate&uuid={ORDER_ID}"
		));
		
		$query = sprintf("UPDATE `%s`
			SET `content` = :content
			WHERE `model` = :model
			AND `foreign_id` = (SELECT `id` FROM `%s` WHERE `key` = :key LIMIT 1)
			AND `field` = :field",
			pjMultiLangModel::factory()->getTable(), pjFieldModel::factory()->getTable()
		);
		pjAppModel::factory()->prepare($query)->exec(array(
			'content' => 'Booking URL - Token: {ORDER_ID}',
			'model' => 'pjField',
			'field' => 'title',
			'key' => 'plugin_invoice_i_booking_url'
		));
		
		$query = sprintf("UPDATE `%s`
			SET `label` = :label
			WHERE `key` = :key
			LIMIT 1",
			pjFieldModel::factory()->getTable()
		);
		pjAppModel::factory()->prepare($query)->exec(array(
			'label' => 'Invoice plugin / Booking URL - Token: {ORDER_ID}',
			'key' => 'plugin_invoice_i_booking_url'
		));
		
		pjLocaleModel::factory()->where("`id` != '1'")->eraseAll();
		pjMultiLangModel::factory()->where("`locale` != '1'")->eraseAll();
	}
	
	protected function pjActionGenerateInvoice($booking_id)
	{
		if (!isset($booking_id) || (int) $booking_id <= 0)
		{
			return array('status' => 'ERR', 'code' => 400, 'text' => 'ID is not set ot invalid.');
		}
		$arr = pjBookingModel::factory()->find($booking_id)->getData();
		if (empty($arr))
		{
			return array('status' => 'ERR', 'code' => 404, 'text' => 'Order not found.');
		}
		
		$bs_arr = pjBookingServiceModel::factory()
			->select("t1.*, t2.content AS `name`")
			->join('pjMultiLang', sprintf("t2.model='pjService' AND t2.foreign_id=t1.service_id AND t2.field='name' AND t2.locale='%u'", $arr['locale_id']), 'left outer')
			->where('t1.booking_id', $booking_id)
			->findAll()
			->getData();
		
		$services = array();
		if (!empty($bs_arr))
		{
			foreach ($bs_arr as $service)
			{
				$services[] = array(
					'name' => $service['name'],
					'description' => NULL,
					'qty' => 1,
					'unit_price' => $service['price'],
					'amount' => number_format(1 * $service['price'], 2, ".", "")
				);
			}
		} else {
			$services[] = array(
				'name' => 'Booking payment',
				'description' => '',
				'qty' => 1,
				'unit_price' => $arr['booking_total'],
				'amount' => $arr['booking_total']
			);
		}
		
		$map = array(
			'confirmed' => 'paid',
			'cancelled' => 'cancelled',
			'pending' => 'not_paid'
		);
		
		$subtotal = $arr['booking_price'];
		$tax = 0;
		if ((float) $this->option_arr['o_tax'] > 0 && $subtotal > 0)
		{
			$tax = ($subtotal * (float) $this->option_arr['o_tax']) / 100;
		}
		
		$config = pjInvoiceConfigModel::factory()->getConfigData($this->getLocaleId());
		$response = $this->requestAction(
			array(
	    		'controller' => 'pjInvoice',
	    		'action' => 'pjActionCreate',
	    		'params' => array(
    				'key' => md5($this->option_arr['private_key'] . PJ_SALT),
					// -------------------------------------------------
					'uuid' => pjInvoiceModel::factory()->getInvoiceID($config),
					'order_id' => $arr['uuid'],
					'foreign_id' => $arr['calendar_id'],
					'issue_date' => ':CURDATE()',
					'due_date' => ':CURDATE()',
					'created' => ':NOW()',
					 'modified' => ':NOW()',
					'status' => 'paid',
					'subtotal' => $subtotal,
					// 'discount' => $arr['discount'],
					'tax' => $tax,
					// 'shipping' => $arr['shipping'],
					'total' => $arr['booking_total'],
					'paid_deposit' => $arr['booking_deposit'],
					'amount_due' => $arr['booking_total'] - $arr['booking_deposit'],
					'currency' => $this->option_arr['o_currency'],
					'notes' => $arr['c_notes'],
					// 'y_logo' => $arr[''],
					// 'y_company' => $arr[''],
					// 'y_name' => $arr[''],
					// 'y_street_address' => $arr[''],
					// 'y_city' => $arr[''],
					// 'y_state' => $arr[''],
					// 'y_zip' => $arr[''],
					// 'y_phone' => $arr[''],
					// 'y_fax' => $arr[''],
					// 'y_email' => $arr[''],
					// 'y_url' => $arr[''],
					'b_billing_address' => $arr['c_address_1'],
					// 'b_company' => ':NULL',
					'b_name' => $arr['c_name'],
					'b_address' => $arr['c_address_1'],
					'b_street_address' => $arr['c_address_2'],
					'b_city' => $arr['c_city'],
					'b_state' => $arr['c_state'],
					'b_zip' => $arr['c_zip'],
					'b_phone' => $arr['c_phone'],
					// 'b_fax' => ':NULL',
					'b_email' => $arr['c_email'],
					// 'b_url' => $arr['url'],
					// 's_shipping_address' => (int) $arr['same_as'] === 1 ? $arr['b_address_1'] : $arr['s_address_1'],
					// 's_company' => ':NULL',
					// 's_name' => (int) $arr['same_as'] === 1 ? $arr['b_name'] : $arr['s_name'],
					// 's_address' => (int) $arr['same_as'] === 1 ? $arr['b_address_1'] : $arr['s_address_1'],
					// 's_street_address' => (int) $arr['same_as'] === 1 ? $arr['b_address_2'] : $arr['s_address_2'],
					// 's_city' => (int) $arr['same_as'] === 1 ? $arr['b_city'] : $arr['s_city'],
					// 's_state' => (int) $arr['same_as'] === 1 ? $arr['b_state'] : $arr['s_state'],
					// 's_zip' => (int) $arr['same_as'] === 1 ? $arr['b_zip'] : $arr['s_zip'],
					// 's_phone' => $arr['phone'],
					// 's_fax' => ':NULL',
					// 's_email' => $arr['email'],
					// 's_url' => $arr['url'],
					// 's_date' => ':NULL',
					// 's_terms' => ':NULL',
					// 's_is_shipped' => ':NULL',
					'items' => $services
					// -------------------------------------------------
    			)
    		),
    		array('return')
		);

		return $response;
	}

	public static function getRawSlots($foreign_id, $date, $type, $option_arr)
	{
		$date_arr = pjDateModel::factory()->getDailyWorkingTime($foreign_id, $date, $type);
		if ($date_arr === false)
		{
			# There is not custom working time/prices for given date, so get for day of week (Monday, Tuesday...)
			$pjWorkingTimeModel = pjWorkingTimeModel::factory();
			$wt_data = $pjWorkingTimeModel->getWorkingTime($foreign_id, $type);
			$wt_arr = $pjWorkingTimeModel->filterDate($wt_data, $date);
			if (empty($wt_arr))
			{
				# It's Day off
				return false;
			}
			// $wt_arr['slot_length'] = $option_arr['slot_length'];
			return $wt_arr;
		} else {
			# There is custom working time/prices for given date
			if (count($date_arr) === 0)
			{
				# It's Day off
				return false;
			}
			return $date_arr;
		}
	}
	
	public static function getSingleDateSlots($calendar_id, $date)
	{
		$dates = pjDateModel::factory()
			->join('pjEmployee', 't2.id = t1.foreign_id')
			->where('t1.type', 'employee')
			->where('t2.calendar_id', $calendar_id)
			->where('t1.date_from <=', $date)
			->where('t1.date_to >=', $date)
			->findAll()
			->getData();

		if (!empty($dates)) {
			foreach ($dates as $k => $v) {
				$d = date('l', strtotime($date));
				$dayOffString = sprintf('%s_dayoff', strtolower($d));

				if ($v[$dayOffString] == 'T') {
					unset($dates[$k]);
				}
			}
		}

		if (!empty($dates)) {
			usort($dates, function ($a, $b) {
				$aStart = new DateTime(date('Y-m-d ' . $a['start_time']));
				$bStart = new DateTime(date('Y-m-d ' . $b['start_time']));

				return ($aStart->getTimestamp() >= $bStart->getTimestamp());
			});
		}

		return $dates;



		$pjDateModel = pjDateModel::factory();
		$pjWorkingTimeModel = pjWorkingTimeModel::factory();
		
		$employee_arr = pjEmployeeModel::factory()
			->where('t1.calendar_id', $calendar_id)
			->where('t1.is_active', 1)
			->findAll()
			->getData();

		foreach ($employee_arr as $key => $employee)
		{
			$employee_arr[$key]['custom'] = $pjDateModel->reset()->getDailyWorkingTime($employee['id'], $date, 'employee');
			$wt_data = $pjWorkingTimeModel->reset()->getWorkingTime($employee['id'], 'employee');
			$employee_arr[$key]['default'] = $pjWorkingTimeModel->filterDate($wt_data, $date);
		}
		
		$general_custom = $pjDateModel->reset()->getDailyWorkingTime($calendar_id, $date, 'calendar');
		
		$start_ts = array();
		$end_ts = array();
		foreach ($employee_arr as $i => $employee)
		{
			if (is_array($employee['custom']) && !empty($employee['custom']))
			{
				$start_ts[$i] = $employee['custom']['start_ts'];
				$end_ts[$i] = $employee['custom']['end_ts'];
				continue;
			}
		
			if (!empty($general_custom))
			{
				$start_ts[$i] = $general_custom['start_ts'];
				$end_ts[$i] = $general_custom['end_ts'];
				continue;
			}

			if (is_array($employee['default']) && !empty($employee['default']))
			{
				$start_ts[$i] = $employee['default']['start_ts'];
				$end_ts[$i] = $employee['default']['end_ts'];
			}
		}
		
		if (empty($start_ts) || empty($end_ts))
		{
			$wt_data = $pjWorkingTimeModel->reset()->getWorkingTime($calendar_id, 'calendar');
			$general_default = $pjWorkingTimeModel->filterDate($wt_data, $date);
			if (empty($start_ts) && !empty($general_default))
			{
				$start_ts[] = $general_default['start_ts'];
			}
			if (empty($end_ts) && !empty($general_default))
			{
				$end_ts[] = $general_default['end_ts'];
			}
		}
		
		return array(
			'start_ts' => min($start_ts),
			'end_ts' => max($end_ts)
		);
	}
	
	public static function getRawSlotsInRange($foreign_id, $date_from, $date_to, $type)
	{
		$date_arr = pjDateModel::factory()->getRangeWorkingTime($foreign_id, $date_from, $date_to, $type);
		
		$pjWorkingTimeModel = pjWorkingTimeModel::factory();
		$wt_data = $pjWorkingTimeModel->getWorkingTime($foreign_id, $type);

		$t_arr = array();
		foreach ($date_arr as $date => $item)
		{
			$t_arr[$date] = array();
			
			# There is not custom working time/prices for given date, so get for day of week (Monday, Tuesday...)
			if (empty($item))
			{
				$wt_arr = $pjWorkingTimeModel->filterDate($wt_data, $date);
				# It's Day off
				if (empty($wt_arr))
				{
					$t_arr[$date]['is_dayoff'] = 'T';
					continue;
				}
				
				$t_arr[$date] = $wt_arr;
				continue;
			}
			
			# Custom day is off
			if ($item['is_dayoff'] == 'T')
			{
				$t_arr[$date]['is_dayoff'] = 'T';
				continue;
			}
			
			$t_arr[$date] = $item;
		}
		
		return $t_arr;
	}
	
	public static function getRawSlotsPerEmployee($employee_id, $date, $cid)
	{
		$pjDateModel = pjDateModel::factory();
		
		# Get custom working time for given employee
		$date_arr = $pjDateModel->getDailyWorkingTime($employee_id, $date, 'employee');
		if ($date_arr !== false)
		{
			# It's Day off
			if (count($date_arr) === 0)
			{
				return false;
			}
			
			# Return custom working time per employee
			return $date_arr;
		}

		return false;
	}
	
	public static function getDatesInRange($calendar_id, $date_from, $date_to)
	{
		# Build date array
		$_arr = array();
		$from = strtotime($date_from);
		$to = strtotime($date_to);
		if ($from > $to)
		{
			$tmp = $from;
			$from = $to;
			$to = $tmp;
		}
		for ($i = $from; $i <= $to; $i += 86400)
		{
			$_arr[date("Y-m-d", $i)] = 'OFF';
		}
		
		$pjDateModel = pjDateModel::factory();
		
		$employee_arr = pjEmployeeModel::factory()
			->where('t1.calendar_id', $calendar_id)
			->where('t1.is_active', 1)
			->findAll()
			->getDataPair('id');

		$stack = array();
		foreach ($_arr as $date => $whatever)
		{
			$now = new DateTime($date);
			$d = date('l', strtotime($date));
			$dayOffString = sprintf('%s_dayoff', strtolower($d));

			$stack[$date] = array();
			foreach ($employee_arr as $key => $employee)
			{
				$stack[$date][] = 'OFF';
				$dates = $pjDateModel
					->reset()
					->where('t1.foreign_id', $employee['id'])
					->where('t1.type', 'employee')
					->where('t1.date_to >=', $date_from)
					->where('t1.date_from <=', $date_to)
					->findAll()
					->getData();
				foreach ($dates as $dateWT) {
					$start = new DateTime($dateWT['date_from']);
					$end = new DateTime($dateWT['date_to']);

					if ($now->getTimestamp() < $start->getTimestamp()) {
						$stack[$date][] = 'OFF';
						continue;
					}
					if ($now->getTimestamp() > $end->getTimestamp()) {
						$stack[$date][] = 'OFF';
						continue;
					}
					if ($dateWT[$dayOffString] == 'F') {
						$stack[$date][] = 'ON';
					}
				}
				
				$dates_off = $pjDateModel
				->reset()
				->where('t1.foreign_id', $employee['id'])
				->where('t1.type', 'employee')
				->where('t1.date_to >=', $date_from)
				->where('t1.date_from <=', $date_to)
				->where('is_dayoff', 'T')
				->findAll()
				->getData();
				
				foreach ($dates_off as $dateWT) {
				    if($date >= $dateWT['date_from'] && $date <= $dateWT['date_to'])
				    {
				        $tmp_arr = $stack[$date];
				        foreach($tmp_arr as $k => $v)
				        {
				            $tmp_arr[$k] = 'OFF';
				        }
				        $stack[$date] = $tmp_arr;
				    }
				}
			}
		}
		
		$result = array();
		foreach ($stack as $date => $values)
		{
			if (in_array('ON', $values))
			{
				$result[$date] = 'ON';
			} else {
				$result[$date] = 'OFF';
			}
		}

		return array($result, $stack);
	}
	
	/* static public function getSingleService($booking, $option_arr)
	{
		$client = pjClientModel::factory()->find($booking['client_id'])->getData();
		$employee = pjEmployeeModel::factory()->find($booking['employee_id'])->getData();

		$bookingDate = pjUtil::convertTimezoneToTimezone($booking['date'], $employee['timezone'], $client['timezone']);
		$bookingStart = pjUtil::convertTimezoneToTimezone(date('Y-m-d H:i:s', $booking['start_ts'] + $booking['before'] * 60), $employee['timezone'], $client['timezone']);
		$bookingEnd = pjUtil::convertTimezoneToTimezone(date('Y-m-d H:i:s', $booking['start_ts'] + $booking['before'] * 60 + $booking['length'] * 60), $employee['timezone'], $client['timezone']);

		$booking_data = stripslashes($booking['service_name']) . ":\n".
			date($option_arr['o_date_format'], strtotime($bookingDate)). "\n".
			date($option_arr['o_time_format'], strtotime($bookingStart)). " - ".
			date($option_arr['o_time_format'], strtotime($bookingEnd));

		return $booking_data;
	} */
	static public function getSingleService($booking, $option_arr)
	{
	    $booking_data = stripslashes($booking['service_name']) . ":\n".
	   	    date($option_arr['o_date_format'], strtotime($booking['date'])). "\n".
	   	    date($option_arr['o_time_format'], $booking['start_ts'] + $booking['before'] * 60). " - ".
	   	    date($option_arr['o_time_format'], $booking['start_ts'] + $booking['before'] * 60 + $booking['length'] * 60);
	   	    
	   	    return $booking_data;
	}
	
	static public function getMultiService($booking, $option_arr)
	{
		//  Before and after time is not included in booking time
		$booking_data = array();
		if (isset($booking['bs_arr']))
		{
			foreach ($booking['bs_arr'] as $item)
			{
				$item['client_id'] = $booking['client_id'];
				$booking_data[] = self::getSingleService($item, $option_arr);
			}
		}

		return join("\n\n", $booking_data);
	}
	
	static public function getTokens($booking, $option_arr, $type='single')
	{
		switch ($type)
		{
			case 'single':
				if(isset($booking['bs_arr']) && (!isset($booking['employee_id']) || (int)$booking['employee_id'] == 0)  && $booking['bs_arr'][0]['employee_id'] > 0){
					$booking['employee_id'] = $booking['bs_arr'][0]['employee_id'];
				}
				$booking_data = pjAppController::getSingleService($booking, $option_arr);
				break;
			case 'multi':
			default:
				$booking_data = pjAppController::getMultiService($booking, $option_arr);
				break;
		}

		$bookingExtras = pjBookingExtraModel::factory()
			->select('t2.name as extra_name, t3.name as option_name, t3.price')
			->where('booking_id', $booking['id'])
			->join('pjExtra', 't2.id = t1.extra_id', 'inner')
			->join('pjExtraOption', 't3.id = t1.extra_option_id')
			->findAll()
			->getData();

		$extras = '';
		if ($bookingExtras) {
			foreach ($bookingExtras as $extra) {
				$extras .= sprintf('%s - %s %s <br>', $extra['extra_name'], $extra['option_name'], pjUtil::formatCurrencySign(number_format($extra['price'], 2), $option_arr['o_currency']));
			}
		}

		$extrasOptions = pjExtraOptionModel::factory()->findAll()->getData();
		$tmp = array();
		foreach ($extrasOptions as $extrasOption) {
			$tmp[$extrasOption['extra_id']][] = $extrasOption;
		}

		$cc = $booking['payment_method'] == 'creditcard';
		$cancelURL = PJ_INSTALL_URL . 'index.php?controller=pjFrontEnd&action=pjActionCancel&cid='.$booking['calendar_id'].'&id='.$booking['booking_id'].'&hash='.sha1($booking['booking_id'].$booking['created'].PJ_SALT);
		
		$invoiceURL = '';
		if(isset($booking['uuid']) && !empty($booking['uuid']))
		{
		    $pjInvoiceModel = pjInvoiceModel::factory();
		    $invoice_arr = $pjInvoiceModel->where('t1.order_id', $booking['uuid'])->limit(1)->orderBy("t1.id ASC")->findAll()->getData();
		    if(count($invoice_arr))
		    {
		        $invoiceURL = PJ_INSTALL_URL . "index.php?controller=pjInvoice&action=pjActionView&id=" . $invoice_arr[0]['uuid'] ."&uuid=" . @$booking['uuid'];
		        $invoiceURL = '<a href="'.$invoiceURL.'">'.$invoiceURL.'</a>';
		    }
		}
		$tutor = __('lblNA', true);
		if((int) $booking['client_id'] > 0)
		{
		    $client = pjClientModel::factory()
		    ->select("t1.*, t2.name AS tutor")
		    ->join('pjUser', 't2.id=t1.tutor_id', 'left outer')
		    ->find($booking['client_id'])->getData();
		    if(!empty($client['tutor']))
		    {
		        $tutor = $client['tutor'];
		    }
		}
		
		$search = array(
			'{Name}', '{Email}', '{Phone}', '{Country}', '{City}',
			'{State}', '{Zip}', '{Address1}', '{Address2}', '{Notes}',
			'{CCType}', '{CCNum}', '{CCExpMonth}', '{CCExpYear}', '{CCSec}', '{PaymentMethod}',
			'{Price}', '{Deposit}', '{Total}', '{Tax}', '{Extras}',
		    '{BookingID}', '{Services}', '{CancelURL}', '{InvoiceURL}', '{Tutor}'
		);

		$replace = array(
			pjSanitize::clean(@$booking['c_name']), pjSanitize::clean(@$booking['c_email']), pjSanitize::clean(@$booking['c_phone']), pjSanitize::clean(@$booking['country_name']), pjSanitize::clean(@$booking['c_city']),
			pjSanitize::clean(@$booking['c_state']), pjSanitize::clean(@$booking['c_zip']), pjSanitize::clean(@$booking['c_address_1']), pjSanitize::clean(@$booking['c_address_2']), @$booking['c_notes'],
			$cc ? $booking['cc_type'] : NULL,
			$cc ? $booking['cc_num'] : NULL,
			$cc ? $booking['cc_exp_month'] : NULL,
			$cc ? $booking['cc_exp_year'] : NULL,
			$cc ? $booking['cc_code'] : NULL,
			$booking['payment_method'],
			pjUtil::formatCurrencySign(number_format($booking['booking_price'], 2), $option_arr['o_currency']),
			pjUtil::formatCurrencySign(number_format($booking['booking_deposit'], 2), $option_arr['o_currency']),
			pjUtil::formatCurrencySign(number_format($booking['booking_total'], 2), $option_arr['o_currency']),
			pjUtil::formatCurrencySign(number_format($booking['booking_tax'], 2), $option_arr['o_currency']),
			$extras,
		    $booking['uuid'], $booking_data, '<a href="'.$cancelURL.'">'.$cancelURL.'</a>', $invoiceURL, $tutor);
		
		return compact('search', 'replace');
	}
	
	static public function getBookedDates($year, $month, $foreign_id, $locale_id)
	{
	    $booked_dates = array();
	    $employee_id = null;
	    $service_id = null;
	    $employee = pjEmployeeModel::factory()->limit(1)->findAll()->getDataIndex(0);
	    if(!empty($employee))
	    {
	        $employee_id = $employee['id'];
	        $service = pjEmployeeServiceModel::factory()->where('employee_id', $employee_id)->limit(1)->findAll()->getDataIndex(0);
	        if(!empty($service))
	        {
	            $service_id = $service['service_id'];
	        }
	    }
	    if($employee_id != null && $service_id != null)
	    {
	        $date = date("Y-m-d", mktime(0, 0, 0, $month, 1, $year));
	        $end_month_date = date("Y-m-d", mktime(0, 0, 0, $month+1, 0, $year));
	        while (strtotime($date) <= strtotime($end_month_date)) {
	            $app_info = pjAppController::getAppointmentInfo($employee_id, $service_id, $foreign_id, $date, $locale_id);
	            $employee = $app_info['employee'];
	            $available_slots = count($employee['dates']);
	            $booked_slots = 0;
	            foreach ($employee['dates'] as $dateWT)
	            {
	                $i = strtotime($dateWT['date_from'] . ' ' . $dateWT['start_time']);
	                foreach ($employee['bs_arr'] as $item)
	                {
	                    if ($i >= $item['start_ts'] && $i < $item['start_ts'] + $item['total'] * 60)
	                    {
	                        $booked_slots++;
	                        break;
	                    }
	                }
	            }
	            if($available_slots == $booked_slots && $booked_slots > 0)
	            {
	                $booked_dates[] = $date;
	            }
	            $date = date ("Y-m-d", strtotime("+1 day", strtotime($date)));
	        }
	    }
	    return $booked_dates;
	}
	
	static public function getWTimePerEmployee($employee_id, $cid, $date = NULL)
	{
		if($date == NULL)
		{
			$date = date('Y-m-d');
		}
		$bs_arr = pjBookingServiceModel::factory()
			->join('pjBooking', "t1.booking_id=t2.id AND t2.booking_status<>'cancelled'", 'inner')
			->where('t1.employee_id', $employee_id)
			->where('t1.date', $date)
			->findAll()
			->getData();
		
		$t_arr = pjAppController::getRawSlotsPerEmployee($employee_id, $date, $cid);
		$dates = pjDateModel::factory()
			->where('t1.foreign_id', $employee_id)
			->where('t1.date_from <=', $date)
			->where('t1.date_to >=', $date)
			->findAll()
			->getData();

		if (!empty($dates)) {
			foreach ($dates as $key => $value) {
				$d = date('l', strtotime($date));
				$dayOffString = sprintf('%s_dayoff', strtolower($d));

				if ($value[$dayOffString] == 'T') {
					unset($dates[$key]);
					continue;
				}

				$dates[$key]['date_from'] = $date;

				$start = new DateTime($value['date_from'] . ' ' . $value['start_time']);
				$end = new DateTime($value['date_to'] . ' ' . $value['end_time']);

				$diff = $start->diff($end);
				$dates[$key]['_length'] = $diff->h * 60 + $diff->i;
			}

			if (!empty($dates)) {
				usort($dates, function ($a, $b) {
					$aStart = new DateTime(date('Y-m-d ' . $a['start_time']));
					$bStart = new DateTime(date('Y-m-d ' . $b['start_time']));

					return ($aStart->getTimestamp() >= $bStart->getTimestamp());
				});
			}
		}

		return compact('t_arr', 'bs_arr', 'dates');
	}
	
	static public function getAppointmentInfo($employee_id, $service_id, $cid, $date, $locale_id)
	{
		$employee = pjEmployeeModel::factory()
			->select("t1.*, t2.content AS `name`")
			->join('pjMultiLang', "t2.model='pjEmployee' AND t2.foreign_id=t1.id AND t2.field='name' AND t2.locale='".$locale_id."'", 'left outer')
			->where('t1.calendar_id', $cid)
			->find($employee_id)
			->getData();
		
		$service = pjServiceModel::factory()
			->select("t1.*, t2.content AS `name`, t3.content AS `description`")
			->join('pjMultiLang', "t2.model='pjService' AND t2.foreign_id=t1.id AND t2.locale='".$locale_id."' AND t2.field='name'", 'left outer')
			->join('pjMultiLang', "t3.model='pjService' AND t3.foreign_id=t1.id AND t3.locale='".$locale_id."' AND t3.field='description'", 'left outer')
			->where('t1.calendar_id', $cid)
			->where('t1.is_active', 1)
			->orderBy('`name` ASC')
			->find($service_id)
			->getData();
		
		$wt_arr = pjAppController::getWTimePerEmployee($employee_id, $cid, $date);
		$employee['t_arr'] = $wt_arr['t_arr'];
		$employee['bs_arr'] = $wt_arr['bs_arr'];
		$employee['dates'] = $wt_arr['dates'];
			
		return compact('employee', 'service');
	}
	
	static public function getFromEmail()
	{
		$arr = pjUserModel::factory()
			->findAll()
			->orderBy("t1.id ASC")
			->limit(1)
			->getData();
		return !empty($arr) ? $arr[0]['email'] : null;
	}
	
	static public function getAdminEmail()
	{
		$arr = pjUserModel::factory()
			->where('t1.is_notify', 'T')
			->findAll()
			->getDataPair('id', 'email');
		return $arr;
	}
	
	static public function appendXmlNode($domDocument, $parentNode, $name, $value) 
	{
	    $childNode      = $domDocument->createElement($name);
	    $childNodeValue = $domDocument->createTextNode($value);
	    $childNode->appendChild($childNodeValue);
	    $parentNode->appendChild($childNode);
	}
	
	static public function sendXMLviaCurl($xmlRequest,$gatewayURL) 
	{
	    $ch = curl_init(); // Initialize curl handle
	    curl_setopt($ch, CURLOPT_URL, $gatewayURL); // Set POST URL
	    
	    $headers = array();
	    $headers[] = "Content-type: text/xml";
	    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); // Add http headers to let it know we're sending XML
	    $xmlString = $xmlRequest->saveXML();
	    curl_setopt($ch, CURLOPT_FAILONERROR, 1); // Fail on errors
	    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // Allow redirects
	    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // Return into a variable
	    curl_setopt($ch, CURLOPT_PORT, 443); // Set the port number
	    curl_setopt($ch, CURLOPT_TIMEOUT, 30); // Times out after 30s
	    curl_setopt($ch, CURLOPT_POST, 1);
	    curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlString); // Add XML directly in POST
	    
	    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
	    
	    
	    // This should be unset in production use. With it on, it forces the ssl cert to be valid
	    // before sending info.
	    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
	    
	    if (!($data = curl_exec($ch))) {
	        print  "curl error =>" .curl_error($ch) ."\n";
	        throw New Exception(" CURL ERROR :" . curl_error($ch));
	        
	    }
	    curl_close($ch);
	    
	    return $data;
	}
}
?>