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

	/**
	 * Generate (or return existing) CSRF token for the current session.
	 */
	public static function getCsrfToken()
	{
		if (empty($_SESSION['csrf_token']))
		{
			$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
		}
		return $_SESSION['csrf_token'];
	}

	/**
	 * Validate CSRF token. pjInput nulls raw $_POST/$_GET, so read request objects first.
	 */
	public static function validateCsrfToken()
	{
		$token = '';
		$reg = pjRegistry::getInstance();
		$post = $reg->get('_post');
		$get = $reg->get('_get');
		if (is_object($post) && $post->check('csrf_token')) {
			$token = (string) $post->toString('csrf_token');
		} elseif (is_object($get) && $get->check('csrf_token')) {
			$token = (string) $get->toString('csrf_token');
		} elseif (!empty($_POST['csrf_token'])) {
			$token = $_POST['csrf_token'];
		} elseif (!empty($_GET['csrf_token'])) {
			$token = $_GET['csrf_token'];
		} elseif (!empty($_SERVER['HTTP_X_CSRF_TOKEN'])) {
			$token = $_SERVER['HTTP_X_CSRF_TOKEN'];
		}
		if (empty($_SESSION['csrf_token']) || !hash_equals($_SESSION['csrf_token'], (string) $token))
		{
			header('HTTP/1.1 403 Forbidden');
			exit('Invalid CSRF token.');
		}
	}

    public function pjActionCheckInstall()
    {
        $this->setLayout('pjActionEmpty');

        $result = array('status' => 'OK', 'code' => 200, 'text' => 'Operation succeeded', 'info' => array());
        $folders = array('app/web/upload');
        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;
    }
	
    /**
     * Sets some predefined role permissions and grants full permissions to Admin.
     */
    public function pjActionAfterInstall()
    {
        $this->setLayout('pjActionEmpty');

        $result = array('status' => 'OK', 'code' => 200, 'text' => 'Operation succeeded', 'info' => array());

        $pjAuthRolePermissionModel = pjAuthRolePermissionModel::factory();
        $pjAuthUserPermissionModel = pjAuthUserPermissionModel::factory();

        $permissions = pjAuthPermissionModel::factory()->findAll()->getDataPair('key', 'id');

        $roles = array(1 => 'admin', 2 => 'editor');
        foreach ($roles as $role_id => $role)
        {
            if (isset($GLOBALS['CONFIG'], $GLOBALS['CONFIG']["role_permissions_{$role}"])
                && is_array($GLOBALS['CONFIG']["role_permissions_{$role}"])
                && !empty($GLOBALS['CONFIG']["role_permissions_{$role}"]))
            {
                $pjAuthRolePermissionModel->reset()->where('role_id', $role_id)->eraseAll();

                foreach ($GLOBALS['CONFIG']["role_permissions_{$role}"] as $role_permission)
                {
                    if($role_permission == '*')
                    {
                        // Grant full permissions for the role
                        foreach($permissions as $key => $permission_id)
                        {
                            $pjAuthRolePermissionModel->setAttributes(compact('role_id', 'permission_id'))->insert();
                        }
                        break;
                    }
                    else
                    {
                        $hasAsterix = strpos($role_permission, '*') !== false;
                        if($hasAsterix)
                        {
                            $role_permission = str_replace('*', '', $role_permission);
                        }

                        foreach($permissions as $key => $permission_id)
                        {
                            if($role_permission == $key || ($hasAsterix && strpos($key, $role_permission) !== false))
                            {
                                $pjAuthRolePermissionModel->setAttributes(compact('role_id', 'permission_id'))->insert();
                            }
                        }
                    }
                }
            }
        }
        pjAuthRoleModel::factory()->setAttributes(array('id' => 3, 'role' => 'Client', 'is_backend' => 'F', 'T'))->insert();
        
		// Grant full permissions to Admin
        $user_id = 1; // Admin ID
        $pjAuthUserPermissionModel->reset()->where('user_id', $user_id)->eraseAll();
        foreach($permissions as $key => $permission_id)
        {
            $pjAuthUserPermissionModel->setAttributes(compact('user_id', 'permission_id'))->insert();
        }

        return $result;
    }
    
    public function isEditor()
	{
		return $this->getRoleId() == 2;
	}
	public function isClient()
	{
	    return $this->getRoleId() == 3;
	}
	
    public function beforeFilter()
    {
        parent::beforeFilter();

        if(!in_array($this->_get->toString('controller'), array('pjFront')))
        {
            $this->appendJs('pjAdminCore.js');
            $this->appendCss('admin.css');
        }
        return true;
    }
    
    public function getForeignId()
    {
    	return 1;
    }
    
    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 getDirection()
	{
		$dir = 'ltr';
		if($this->getLocaleId() != false)
		{
			$locale_arr = pjLocaleModel::factory()->find($this->getLocaleId())->getData();
			$dir = $locale_arr['dir'];
		}
		return $dir;
	}
    
	protected static function getAdminEmail()
	{
	    $arr = pjAuthUserModel::factory()->select('t1.email')->find(1)->getData();
	    
	    return $arr ? $arr['email'] : NULL;
	}
	protected static function getAdminPhone()
	{
	    $arr = pjAuthUserModel::factory()->select('t1.phone')->find(1)->getData();
	    
	    return $arr ? $arr['phone'] : NULL;
	}
	
	public function friendlyURL($str, $divider='-')
	{
		$str = mb_strtolower($str, mb_detect_encoding($str));
		$str = trim($str);
		$str = preg_replace('/[_|\s]+/', $divider, $str);
		$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);
		$str = preg_replace('/[-]+/', $divider, $str);
		$str = preg_replace('/^-+|-+$/', '', $str);
		return $str;
	}
	
	static public function  getRentalDays($option_arr, $date_from, $date_to, $hour_from=null, $hour_to=null, $minutes_from=null, $minutes_to=null)
	{
	    $datetime1 = date_create($date_from);
	    $datetime2 = date_create($date_to);
	    $interval = date_diff($datetime1, $datetime2);
	    
		switch ($option_arr['o_price_based_on'])
		{
			case 'Day':
				/* from 24.12.2012 to 25.12.2012 = 2 Days */
				$rental_days = 1 + (int) $interval->format('%a');
				break;
			case 'Night':
				/* from 24.12.2012 to 25.12.2012 = 1 Day */
			    $rental_days = (int) $interval->format('%a');
				break;
			case '24Hour':
			default:
				/* from 9 AM at 24.12.2012 to 9 AM at 25.12.2012 = 1 Day
				   from 9 AM at 24.12.2012 to 10 AM at 25.12.2012 = 2 Days */
				$rental_days = ceil((strtotime($date_to . " " . $hour_to . ":" . $minutes_to) - strtotime($date_from. " " . $hour_from . ":" . $minutes_from)) / 86400);
				break;
		}
		return $rental_days > 0 ? $rental_days : 1;
	}
	
	static public function getPrice($option_arr, $space_id, $from_ts, $to_ts, $rental_days, $locale_id, $code=null, $extras=null)
	{
		$date_from = date('Y-m-d', $from_ts);
		$date_to = date('Y-m-d', $to_ts);
		$extra_compilation = pjExtraModel::getPrice($rental_days, $locale_id, $extras);
		$extra_price = pjCurrency::formatPriceOnly($extra_compilation['extra_price']);
		
		$code_arr = array();
		if (!is_null($code))
		{
			if (pjObject::getPlugin('pjVouchers') !== NULL)
            {
                $result = pjVoucherModel::factory()->getVoucher($code, $date_from, date('H:i:s', $from_ts));
                if($result['status'] == 'OK')
                {
                    $code_arr = $result['arr'];
                }
            }
		}
		$price_arr =  pjPriceModel::getPrice($space_id, $date_from, $date_to, $rental_days);
		$price = pjCurrency::formatPriceOnly($price_arr['price']) + $extra_price;
		
		if (count($code_arr) > 0)
		{
			switch ($code_arr['type'])
			{
				case 'percent':
					$price_after_discount = $price - (($price * $code_arr['discount']) / 100);
					$price_after_discount = $price_after_discount > 0 ? $price_after_discount : 0;
					break;
				case 'amount':
					$price_after_discount = $price - $code_arr['discount'] > 0 ? $price - $code_arr['discount'] : 0;
					break;
			}
			$tax = ($price_after_discount * $option_arr['o_tax_payment']) / 100;
			$total = $price_after_discount + $tax;
			$deposit = ($total * ((float) $option_arr['o_deposit_payment'])) / 100;
				
			$p = array(
				'type' => $code_arr['type'],
				'discount' => $code_arr['discount'],
			    'discount_formatted' => $code_arr['type'] == 'amount' ? pjCurrency::formatPrice($code_arr['discount']) : (float) $code_arr['discount'] .'%',
				'rental_price' => round($price_arr['price'], 2),
			    'rental_price_formatted' => pjCurrency::formatPrice($price_arr['price']),
				'price_before_discount' => round($price, 2),
				'price_after_discount' => round($price_after_discount, 2),
			    'price_before_formatted' => pjCurrency::formatPrice($price),
			    'price_after_formatted' => pjCurrency::formatPrice($price_after_discount),
			    'total' => round($total, 2),
		        'total_formatted' => pjCurrency::formatPrice($total),
				'deposit' => round($deposit, 2),
			    'deposit_formatted' => pjCurrency::formatPrice($deposit),
				'tax' => round($tax, 2),
			    'tax_formatted' => pjCurrency::formatPrice($tax),
				'extra_compilation' => $extra_compilation
			);
		}else{
			$tax = ($price * $option_arr['o_tax_payment']) / 100;
			$total = $price + $tax;
			$deposit = ($total * ((float) $option_arr['o_deposit_payment'])) / 100;
			
			$p = array(
				'rental_price' => round($price_arr['price'], 2),
			    'rental_price_formatted' => pjCurrency::formatPrice($price_arr['price']),
				'price' => round($price, 2),
			    'price_formatted' => pjCurrency::formatPrice($price),
				'total' => round($total, 2),
			    'total_formatted' => pjCurrency::formatPrice($total),
			    'deposit' => round($deposit, 2),
			    'deposit_formatted' => pjCurrency::formatPrice($deposit),
			    'tax' => round($tax, 2),
			    'tax_formatted' => pjCurrency::formatPrice($tax),
		        'extra_compilation' => $extra_compilation
			);
		}
		return $p;
	}
	
	static public function getSpaces($from_ts, $to_ts, $date_from, $date_to, $rental_days, $locale_id, $sort = null, $booking_id=null)
	{
		$dates = array();
		
		$dt_from = date('Y-m-d H:i:s', $from_ts);
		$dt_to = date('Y-m-d H:i:s', $to_ts);
		
		$from_iso = date('Y-m-d', $from_ts);
		$to_iso = date('Y-m-d', $to_ts);
		$iso = $from_iso;
		while(strtotime($iso) < strtotime($to_iso))
		{
		    $iso = date('Y-m-d', strtotime($iso . ' +1 day'));
		    $dates[date("Y-m-d", strtotime($iso))] = array();
		}
		$pjSpaceModel = pjSpaceModel::factory();
		$pjSpaceDateModel = pjSpaceDateModel::factory();
		$pjBookingModel = pjBookingModel::factory();
		
		$col_name = 'name';
		$direction = 'asc';
		if (!empty($sort) && strpos($sort, "_") !== false)
		{
			list($col_name, $direction) = explode("_", $sort);
		}
		
		$space_arr = $pjSpaceModel
			->join('pjMultiLang', "t2.foreign_id = t1.id AND t2.model = 'pjSpace' AND t2.locale = '".$locale_id."' AND t2.field = 'name'", 'left')
			->join('pjMultiLang', "t3.foreign_id = t1.id AND t3.model = 'pjSpace' AND t3.locale = '".$locale_id."' AND t3.field = 'description'", 'left')
			->select('t1.*, t2.content as name, t3.content as description')
			->where('t1.status', 'T')
			->findAll()
			->getData();
		$space_date_arr = $pjSpaceDateModel
			->findAll()
			->getData();
		foreach ($space_arr as $k => $space)
		{
			$space_arr[$k]['dates'] = $dates;
			foreach ($dates as $date => $val)
			{
				$ts = strtotime($date);
				$pjBookingModel->reset();
				if($booking_id != null)
				{
					$pjBookingModel->where("t1.id <>", $booking_id);
				}
				$bookings = $pjBookingModel
				
					->where('t1.space_id', $space['id'])
					->where('t1.status <>', 'cancelled')
					->where(sprintf("(t1.from <= '%2\$s' AND t1.to >= '%1\$s')", $dt_from, $dt_to))
					->findCount()
					->getData();
				$space_arr[$k]['dates'][$date]['bookings'] = $bookings;
				foreach ($space_date_arr as $space_date)
				{
					if ($space_date['space_id'] == $space['id'] && $ts >= strtotime($space_date['date_from']) && $ts <= strtotime($space_date['date_to']))
					{
						$space_arr[$k]['dates'][$date]['spaces'] = $space_date['spaces'];
						break;
					}
				}
			}
		}
		foreach ($space_arr as $k => $space)
		{
			$space_arr[$k]['is_available'] = 1;
			foreach ($space['dates'] as $date => $item)
			{
				if (!isset($item['spaces']) || (int) $item['bookings'] >= (int) $item['spaces'])
				{
					$space_arr[$k]['is_available'] = 0;
					break;
				}
			}
		}
		$id = $name = $description = $price = $save = array();
		foreach ($space_arr as $k => $space)
		{
			$id[$k] = $space['id'];
			$name[$k] = $space['name'];
			$description[$k] = $space['description'];
			$p_arr = pjPriceModel::getPrice($space['id'], $date_from, $date_to, $rental_days);
			$space_arr[$k]['price'] = $p_arr['price'];
			$space_arr[$k]['save'] = $p_arr['save'];
			$price[$k] = $space_arr[$k]['price'];
			$save[$k] = $space_arr[$k]['save'];
		}
		
		switch ($col_name)
		{
			case 'name':
				switch ($direction)
				{
					case 'asc':
						array_multisort($name, SORT_ASC, SORT_STRING, $price, SORT_ASC, SORT_NUMERIC, $save, SORT_DESC, SORT_NUMERIC, $id, SORT_ASC, SORT_NUMERIC, $description, $space_arr);
						break;
					case 'desc':
						array_multisort($name, SORT_DESC, SORT_STRING, $price, SORT_ASC, SORT_NUMERIC, $save, SORT_DESC, SORT_NUMERIC, $id, SORT_ASC, SORT_NUMERIC, $description, $space_arr);
						break;
				}
				break;
			case 'price':
				switch ($direction)
				{
					case 'asc':
						array_multisort($price, SORT_ASC, SORT_NUMERIC, $save, SORT_DESC, SORT_NUMERIC, $name, SORT_ASC, SORT_STRING, $id, SORT_ASC, SORT_NUMERIC, $description, $space_arr);
						break;
					case 'desc':
						array_multisort($price, SORT_DESC, SORT_NUMERIC, $save, SORT_DESC, SORT_NUMERIC, $name, SORT_ASC, SORT_STRING, $id, SORT_ASC, SORT_NUMERIC, $description, $space_arr);
						break;
				}
				break;
		}
		
		return $space_arr;
	}
	
	static public function getExtras($iso_from, $iso_to, $locale_id, $booking_id=null)
	{
		$where = "";
		if($booking_id != null)
		{
			$where = " `id` <> '$booking_id' AND";
		}
		$extra_arr = pjExtraModel::factory()
			->join('pjMultiLang', "t2.foreign_id = t1.id AND t2.model = 'pjExtra' AND t2.locale = '".$locale_id."' AND t2.field = 'name'", 'left')
			->join('pjMultiLang', "t3.foreign_id = t1.id AND t3.model = 'pjExtra' AND t3.locale = '".$locale_id."' AND t3.field = 'description'", 'left')
			->select(sprintf("t1.*, t2.content AS name, t3.content as description, 
									  (SELECT SUM(`cnt`)
									   FROM `%1\$s`
									   WHERE `extra_id` = `t1`.`id`
									   AND `booking_id` IN ( SELECT `id` FROM `%2\$s` WHERE%5\$s (`status` = 'confirmed' OR `status` = 'pending') 
										   AND ( ('%3\$s' BETWEEN DATE(`from`) AND DATE(`to`)) OR ('%4\$s' BETWEEN DATE(`from`) AND DATE(`to`)) OR (DATE(`from`) BETWEEN '%3\$s' AND '%4\$s') OR (DATE(`to`) BETWEEN '%3\$s' AND '%4\$s') ))
									  ) AS bookings", pjBookingExtraModel::factory()->getTable(), pjBookingModel::factory()->getTable(), $iso_from, $iso_to, $where))
		  	->orderBy("name ASC")
		  	->findAll()
		  	->getData();
		
		$extra_per = __('extra_per', true);
		foreach($extra_arr as $k => $v)
		{
			$v['price_format'] = pjCurrency::formatPrice($v['price']) . ' ' . $extra_per[$v['per']];
			$extra_arr[$k] = $v;
		}
		return $extra_arr;
	}
	
	static public function getTokens($data, $option_arr, $salt, $locale_id)
	{
		$country = NULL;
		$extras = NULL;
		$space = NULL;
		$extra_arr = array();
		
		$extra_per = __('extra_per', true);
		$personal_titles = __('personal_titles', true, false);
		$title = @$personal_titles[$data['c_title']];
		$payment_titles = pjPayments::getPaymentTitles($data['calendar_id'], $locale_id);
		$payment_method = $data['payment_method'];
		if (isset($payment_titles[$data['payment_method']])) {
			$payment_method = $payment_titles[$data['payment_method']];
		}
		
		$space_arr = pjSpaceModel::factory()
			->join('pjMultiLang', "t2.foreign_id = t1.id AND t2.model = 'pjSpace' AND t2.locale = '".$locale_id."' AND t2.field = 'name'", 'left')
			->select('t1.*, t2.content as name')
			->find($data['space_id'])
			->getData();
		$space = $space_arr['name'] . ' (' . pjCurrency::formatPrice($data['rental_price']) . ')';
		
		if(isset($data['c_country']) && (int) $data['c_country'] > 0)
		{
		 	$country_arr = pjBaseCountryModel::factory()
            ->select('t1.id, t2.content AS country_title')
            ->join('pjBaseMultiLang', "t2.model='pjBaseCountry' AND t2.foreign_id=t1.id AND t2.field='name' AND t2.locale='".$locale_id."'", 'left outer')
            ->find($data['c_country'])->getData();
            if (!empty($country_arr))
            {
                $country = $country_arr['country_title'];
            }
		}
		$be_arr = pjBookingExtraModel::factory()
			->select('t1.*, t3.content as name, t2.per, t2.price as unit_price')
			->join('pjExtra', "t2.id=t1.extra_id", 'left')
			->join('pjMultiLang', "t3.foreign_id = t1.extra_id AND t3.model = 'pjExtra' AND t3.locale = '".$locale_id."' AND t3.field = 'name'", 'left')
			->where('t1.booking_id', $data['id'])
			->findAll()
			->getData();

		foreach($be_arr as $k => $v)
		{
			$extra_arr[] = $v['name'] . ' (' . pjCurrency::formatPrice($v['unit_price']) . ' ' . $extra_per[$v['per']] .  ') x ' . $v['cnt'] . ' = ' . pjCurrency::formatPrice($v['price']);
		}
		$extras = join("\r\n", $extra_arr);
		
		$tax = pjCurrency::formatPrice($data['tax']);
		$deposit = pjCurrency::formatPrice($data['deposit']);
		$total = pjCurrency::formatPrice($data['total']);
		
		$cancelURL = PJ_INSTALL_URL . 'index.php?controller=pjFront&action=pjActionCancel&id='.@$data['id'].'&hash='.sha1(@$data['id'].@$data['created'].$salt);
		$cancelURL = '<a href="'.$cancelURL.'">' . $cancelURL . '</a>';
		$from = date($option_arr['o_date_format'], strtotime($data['from'])) . ', ' . date($option_arr['o_time_format'], strtotime($data['from']));
		$to = date($option_arr['o_date_format'], strtotime($data['to'])) . ', ' . date($option_arr['o_time_format'], strtotime($data['to']));
		
		$search = array(
			'{Title}',
			'{Name}',
			'{Email}',
			'{Phone}',
			'{Country}',
			'{City}',
			'{State}',
			'{Zip}',
			'{Address}',
			'{Company}',
			'{Notes}',
			'{RegNo}',
			'{Make}',
			'{Model}',
			'{DtFrom}',
			'{DtTo}',
			'{DateFrom}',
			'{DateTo}',
			'{Space}',
			'{Extras}',
			'{BookingID}',
			'{UniqueID}',
			'{Deposit}',
			'{Total}',
			'{Tax}',
			'{PaymentMethod}',
			'{CCType}',
			'{CCNum}',
			'{CCExp}',
			'{CCSec}',
			'{CancelURL}'
		);
		$replace = array(
			$title,
			pjSanitize::clean(@$data['c_name']),
		    pjSanitize::clean(@$data['c_email']),
		    pjSanitize::clean(@$data['c_phone']),
			$country,
		    pjSanitize::clean(@$data['c_city']),
		    pjSanitize::clean(@$data['c_state']),
		    pjSanitize::clean(@$data['c_zip']),
		    pjSanitize::clean(@$data['c_address']),
		    pjSanitize::clean(@$data['c_company']),
		    pjSanitize::clean(@$data['c_notes']),
		    pjSanitize::clean(@$data['c_regno']),
		    pjSanitize::clean(@$data['c_make']),
			@$data['c_model'],
			$from,
			$to,
			$from,
			$to,
			$space,
			$extras,
			@$data['id'],
			@$data['uuid'],
			$deposit,
			$total,
			$tax,
			$payment_method,
			@$data['cc_type'],
			@$data['cc_num'],
			@$data['cc_exp_month'] . '-' . @$data['cc_exp_year'],
			@$data['cc_code'],
			$cancelURL
		);
		return compact('search', 'replace');
	}
}
?>