<?php
namespace Controllers;
//! Base controller
class Controller {

	public $db, $f3, $model, $controller, $users, $messages, $logs, $cd, $sets, $api_data;
	
	function beforeroute() {

	}

	//! HTTP route post-processor
	function afterroute() {
		// Render HTML layout
		$model=$this->f3->get('PARAMS.model');
		$script='';
		// если у модели есть свой js, то сформируем строку для его подключения
		if(file_exists('ui/scripts/'.$model.'.js')) {
			$script='<script type="text/javascript" src="/ui/scripts/'.$model.'.js?v='.$this->f3->get('site_version').'"></script>';
		}
		$show_menu_array=array(
			'/',
			'/receipts/sales',
			'/receipts/return',
			'/receipts/writeoff',
			'/receipts/pricetags',
			'/settings/start',
			'/settings/rooms',
			'/settings/base',
			'/settings/autonomous'
		);

		$show_menu=false;
		$uri=$this->f3->get('URI');
		$n=mb_strpos($uri, '?');
		if($n>0) {
			$uri=mb_substr($uri, 0, $n);
		}
		if(in_array($uri, $show_menu_array) || (int)$this->f3->get('GET.show_menu')==1) {
			$show_menu=true;
		}

		if((!$this->f3->get('AJAX') || $show_menu) && (!$this->f3->get('POST') || $uri=='/settings/start') || $this->f3->get('GET.not_ajax')) { // если страница вызвана не аяксом, и не надо показывать меню, то загружаем полностью шаблон
			$this->f3->set('menu', $this->controller->get_main_menu($this->sets, $this->db));
			$layout='layout';
			if($this->f3->get('authorization_user.kiosk')==1) {
				$layout='kiosk/'.$this->sets['kiosk_type'].'/layout';
			}
			elseif(!isset($this->sets['dark_theme']) || (int)$this->sets['dark_theme']==1) {
				$layout.='_dark';
			}
			echo \Template::instance()->render($layout.'.htm')."\n\r".$script;
		}
		else { // а если аяксом то пытаемся показать шаблон только нужной части
			if($this->f3->get('page_data.inc'))	{
				$inc=$this->f3->get('page_data.inc');
				if($this->f3->get('authorization_user.kiosk')==1 && file_exists($this->f3->get('ROOT').'/ui/kiosk/'.$this->sets['kiosk_type'].'/'.$inc)) {
					echo \Template::instance()->render('kiosk/'.$this->sets['kiosk_type'].'/'.$inc)."\n\r".$script;
				}
				else {
					echo \Template::instance()->render($inc)."\n\r".$script;
				}
			}
		}
	}

	//! Instantiate class
	public function __construct() {
		$this->f3 = \Base::instance(); // f3
		if(!$this->f3->get('PARAMS.model')) {
			$this->f3->set('PARAMS.model', 'receipts');
		}
		if(!$this->f3->get('PARAMS.method')) {
			$this->f3->set('PARAMS.method', 'sales');
		}
		$this->controller=new \Models\Controller($this->f3); // модель для всех контроллеров
		$this->messages=new \Models\Messages; // модель с ошибками и сообщениями
		$db_name=$this->f3->get('db_name'); // имя базы данных
		$path=$this->f3->get('PATH');

		if(!file_exists('app/api_config.php')) {
			if(file_exists($db_name)) {
				unlink($db_name);
			}
			if($path!='/settings/start' && $path!='/settings/api' && $path!='/settings/autonomous' && $path!='/api/searchsubdomain'  && $path!='/api/getstores') {
				if($this->f3->get('AJAX')) {
					echo "<script>location.reload();</script>";
					exit();
				}
				else {
					$this->f3->reroute('/settings/start');
				}
			}
			return;
		}

		$create_database=0;
		if(!file_exists($db_name)) { // проверяем наличие базы
			if($path!='/') {
				$this->f3->reroute('/');
				return;
			}
			$res=$this->controller->create_database($this->f3->get('db_instance'), $db_name); // при необходимости создаем базу
			if($res['success']) {
				$this->db=$res['db'];
				$create_database=1;
			}
			else {
				$this->f3->set('page_data', $this->messages->message($res['txt'])); // ошибка, если не удалось создать базу
				if($res['reroute']) {
					$this->f3->reroute($res['reroute']);
				}
				$this->afterroute();
				exit();
			}
		}
		else {
			$this->db=new \DB\SQL('sqlite:'.$db_name);
		}
		$s=new \Models\Settings($this->f3, $this->db);
		$this->api_data=$this->controller->get_api_config();

		if(!isset($this->api_data['rm_name']) && $this->db) {
			$fr=$this->db->exec("SELECT rm_name FROM fr_settings WHERE rm=? LIMIT 1", $this->api_data['rm_id']);
			if($fr) {
				$this->api_data['rm_name']=$fr[0]['rm_name'];
			}
		}

		$this->f3->set('api_data', $this->api_data);

		// Основные настройки
		$sets=$s->get_base();

		$this->sets=$sets;
		if($create_database==1 && !$sets['autonomous']) {
			$this->f3->reroute('/settings/backups'); // Если создавали БД, то предложим восстановить ее из резервной копии
		}
		
		$this->db->exec('PRAGMA journal_mode=WAL;');//многопоточный режим бд
		$this->db->exec('PRAGMA busy_timeout=10000;'); // 10 секунд если на базе эксклюзивная блокировка

		// Объявляем сессии
		//$this->f3->set('JAR.expire', time()+3600);
		$this->f3->set('JAR.httponly', false);
		$this->f3->set('JAR.path', '/');
		$this->f3->set('JAR.domain', '');

		/*
		new \DB\SQL\Session($this->db,'sessions',TRUE,function(){
  			// Suspect session
  			return true;
		});
		*/
		//если это не апи, то стартуем сессию.
		//апи сбивает работу с данными в сессии
		/*if($path!='/api/auto' && $path!='/cd/receipt' && $path!='/cd/pricechecker' && !session_id()) {
			new \Session(function(){return true;});
		}
		else{
			$session_name=session_name();
			if($this->f3->get("COOKIE.".$session_name)) {
				$this->f3->clear("COOKIE.".$session_name);
			}
		}*/
		$session_name=session_name();
		if($this->f3->get("COOKIE.".$session_name)) {
			$this->f3->clear("COOKIE.".$session_name);
		}
		if(explode('/', $path)[1]=='multikassa') {
			$path='/multikassa/*';
		}
		$this->logs=new \Models\Logs($this->db, $this->f3); // модель логов
		$this->f3->set('logs_model', $this->logs);
		$this->users=new \Models\Users($this->db, $this->f3); // модель пользователей
		$not_autorization=array(
			'/users/autorization',
			'/receipts/walk',
			'/receipts/walk_add_card',
			'/messages/continue',
			'/api/auto',
			'/api/users',
			'/multikassa/*',
			'/products/barcode',
			'/products/addstop',
			'/products/changestop',
			'/products/deletestop',
			'/products/clearstop',
			'/products/searchstop',
			'/cd/receipt',
			'/cd/pricechecker',
			'/receipts/executor_display',
			'/receipts/archive',
			'/receipts/client_display',
			'/receipts/order_complite',
			'/receipts/order_line_complite',
			'/receipts/issued_order',
			'/receipts/issued_order_line',
			'/qr/gen',
			'/api/searchsubdomain',
			'/api/getstores',
			'/settings/manualforprinters',
			'/api/new_version',
			'/clientsscales/updatescale',
			'/settings/backups'
		);
		if(!in_array($path, $not_autorization)) { // если мы не на странице авторизации, то проверяем авторизацию пользователя
			if(!$this->users->is_login() && $path!='/products/stoplist') {
				if($this->f3->get('AJAX') && !$this->f3->get('GET.not_ajax')) {
					echo "<script>location.reload();</script>";
					exit();
				}
				else {
					if($this->f3->get('GET.not_ajax')) {
						$this->f3->reroute('/users/autorization?not_ajax=1');
					}
					else {
						$this->f3->reroute('/users/autorization?'.$this->f3->get('QUERY')); // если пользователь не авторизован, то переходим на страницу авторизации
					}
				}
			}
		}

		$not_access=array(
			'/users/autorization',
			'/receipts/walk',
			'/receipts/walk_add_card',
			'/users/logout',
			'/messages/continue',
			'/api/auto',
			'/api/users',
			'/multikassa/*',
			'/products/barcode',
			'/products/detail',
			'/products/stoplist',
			'/products/addstop',
			'/products/changestop',
			'/products/deletestop',
			'/products/clearstop',
			'/products/searchstop',
			'/cd/receipt',
			'/cd/pricechecker',
			'/receipts/executor_display',
			'/receipts/archive',
			'/receipts/client_display',
			'/receipts/order_complite',
			'/receipts/order_line_complite',
			'/receipts/issued_order',
			'/receipts/issued_order_line',
			'/qr/gen',
			'/users/kiosklogout',
			'/api/searchsubdomain',
			'/api/getstores',
			'/settings/license',
			'/settings/manualforprinters',
			'/api/new_version',
			'/clientsscales/updatescale',
			'/settings/backups',
			'/settings/updatealarm'
		);

		if(!$this->api_data['domen']
			&& (!$sets['autonomous'] && $path!='/api/searchsubdomain' && $path!='/api/getstores')
			&& $path!='/settings/api'
			&& $path!='/settings/autonomous'
			&& $path!='/users/logout'
			&& $path!='/users/autorization'
		) {
			$this->f3->reroute('/settings/api');
		}

		$this->f3->set('base_settings', $sets);
		if($sets['timezone']) {
			$this->f3->set('TZ', $sets['timezone']);
		}
		else {
			$this->f3->set('TZ', 'Europe/Moscow');
		}

		$this->f3->set('neva', $this->controller->get_neva($this->db));

		if($path!='/qr/gen') {
			$request=$path=='/api/auto'?1:0;
			$res=$s->ping($request);
			if(!$res['success']) {
				if(!$res['txt'])$res['txt']=$res['error'][0];
				$this->f3->set('error', $this->messages->message($res['txt'])['txt']);
				$this->f3->set('page_data', $s->page_data('renewlicense'));
				$this->afterroute();
				exit();
			}
		}
		if(!in_array($path, $not_access) && file_exists('app/api_config.php')) {
			$res=$this->controller->access();
			if(!$res['success']) {
				// Если доступ закрыт, и действие вызывается аяксом, то предложим выполнить это действие с помощью другого пользователя, который введет свой пароль, пользователь при этом не авторизуется
				// Если уже предлагали, и пользователь указан, то надо проверить права этого пользователя, и либо выдать ошибку либо разрешить действие
				// Если выдаем ошибку, то больше не предлагаем выбрать другого пользователя
				if($this->f3->get('AJAX')) {
					$data=$this->f3->get('REQUEST');
					if(!isset($data['user_id']) && !isset($data['user_pass'])) { // Если другой пользователь еще не указан
						$this->f3->set('users', $this->users->select_users()); // Список пользователей
						$this->f3->set('page_data', $this->messages->page_data('access_closed'));
						$this->afterroute();
						exit();
					}
					else {
						// Ищем указанного пользователя
						$user=$this->users->select_users(array('u.id=? AND u.pass=?', array($data['user_id'], $data['user_pass'])), array('limit'=>1));
						if(empty($user)) {
							// Если пользователя такого нет, то ошибка
							$this->f3->set('page_data', $this->messages->message('user_not_found'));
							$this->afterroute();
							exit();
						}
						$user[0]['module']=$this->users->users_modules($user[0]['module']); // Получаем права указанного пользователя
						// Проверяем права указанного пользователя
						$res=$this->controller->access($user[0]);
					}
				}

				if(!$res['success']) { // Так как возможно еще одна проверка прав, если был указан другой пользователь, то нужно еще раз сравнить результат проверки
					$this->f3->set('page_data', $this->messages->message($res['txt']));
					$this->afterroute();
					exit();
				}
			}
		}

		// Удаление старых чеков и логов
		if($sets['cleaning']<time()-3600*24) {
			$s->cleaning();
			$this->logs->cleaning_logs();
		}

		if(!$this->f3->get('AJAX') && $this->f3->get('authorization_user')) {
			$this->cd=new \Models\Cd($this->db, $this->f3);
			if($this->f3->get('PARAMS.model')!='receipts' && $this->f3->get('PARAMS.model')!='api' && $this->f3->get('PARAMS.model')!='cd' && $this->f3->get('PARAMS.model')!='multikassa') {
				$this->cd->close_txt();
			}
		}
	}

	// Пагинация
	function pagination($lines_all, $page) {
		$lines_of_page=$this->f3->get('lines_of_page');
		$page_count=ceil($lines_all/$lines_of_page);
		$size=3;
		$pages = array();
		for($i=max($page-$size, 1); $i<=$page_count && $i<=$page+$size; $i++) {
			$pages[]=$i;
		}
		if($pages[0]>3) {
			array_unshift($pages, 1, '...');
		}
		elseif($pages[0]==3) {
			array_unshift($pages, 1, 2);
		}
		elseif($pages[0]==2) {
			array_unshift($pages, 1);
		}
		$n=count($pages)-1;
		if($pages[$n]<$page_count-2) {
			$pages[$n+1]='...';
			$pages[$n+2]=$page_count;
		}
		elseif($pages[$n]==$page_count-2) {
			$pages[$n+1]=$page_count-1;
			$pages[$n+2]=$page_count;
		}
		elseif($pages[$n]==$page_count-1) {
			$pages[$n+1]=$page_count;
		}
		$url=$this->f3->get('URI');
		if($this->f3->get('GET.page')) {
			$url=str_replace('page='.$page, 'page=', $url);
		}
		elseif($this->f3->get('GET')) {
				$url.='&page=';
		}
		else {
			$url.='?page=';
		}
		return array(
			'pages'=>$pages,
			'url'=>$url,
			'page_count'=>$page_count
		);
	}

	function multi_curl_range($start, $end, $path, $port='') {
		$start=ip2long($start);
		$end=ip2long($end);
		$range=array();
		for($ip=$start; $ip<=$end; $ip++) {
			$range[]=long2ip($ip);
		}
		return $this->multi_curl($range, $path, $port);
	}

	function multi_curl($range, $path, $port='') {
		$data=array();
		$curl_arr=array();
		$curl=curl_multi_init();
		if($port) {
			$ports=explode(',', str_replace(' ', '', $port));
		}
		else {
			$ports[0]='';
		}
		$range_port=array();
		foreach($range as $ip){
			if(mb_strpos($ip, ":")>0) {
				$range_port[]=$ip;
			}
			else {
				foreach($ports as $p) {
					$range_port[]=$ip.':'.$p;
				}
			}
		}

		foreach($range_port as $ip_port) {
			$url=$ip_port.$path;
			$curl_arr[$ip_port]=curl_init($url);
			curl_setopt($curl_arr[$ip_port], CURLOPT_RETURNTRANSFER, true);
			curl_setopt($curl_arr[$ip_port], CURLOPT_HEADER, false);
			curl_setopt($curl_arr[$ip_port], CURLOPT_TIMEOUT, 5);
			curl_setopt($curl_arr[$ip_port], CURLOPT_CONNECTTIMEOUT, 2);
    		$r=curl_multi_add_handle($curl, $curl_arr[$ip_port]);
		}
		do {
			curl_multi_exec($curl,$running);
			if($running>0) {
				curl_multi_select($curl,1);
			}
		}
		while($running>0);
		$result=array();
		foreach($range_port as $ip_port) {
			$res=curl_multi_getcontent($curl_arr[$ip_port]);
   			if($res) {
   				$result[$ip_port]=$res;
   			}
		}
		curl_multi_close($curl);
		return $result;
	}
}
