<?php

/**
 * Vvveb
 *
 * Copyright (C) 2020  Ziadin Givan
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

namespace Vvveb\System;

class FrontController {
	/**
	 * Standard Controller constructor.
	 */
	static private $moduleName;

	static private $actionName;

	static private $app = 'app';

	static public $rewrite;

	/**
	 * Don't allow construction of the controller (this is a singleton).
	 *
	 */
	private function __construct() {
	}

	/**
	 * Don't allow cloning of the controller (this is a singleton).
	 *
	 */
	private function __clone() {
	}

	/**
	 * Returns current controller name.
	 *
	 * @return string
	 */
	function getModuleName() {
		return self :: $moduleName;
	}

	static function app($app = null) {
		if ($app) {
			self :: $app = $app;
		}

		return self :: $app;
	}

	/**
	 * Returns current controller name.
	 *
	 * @return string
	 */
	static function getActionName() {
		return self :: $actionName;
	}

	static function notFound($service = true, $statusCode = 404, $message = false) {
		//header(' ', true, $statusCode);
		if (include_once DIR_APP . "/controller/error$statusCode.php") {
			$controller         = 'Vvveb\Controller\Error' . $statusCode;
			self :: $moduleName = $moduleName = 'error' . $statusCode;
			$controller         = 'Vvveb\Controller\Error' . $statusCode;
			header("HTTP/1.0 $statusCode" /* . substr(str_replace(($message ?? ''), "\n", ' '), 100)*/);
		} else {
			header(' ', true, $statusCode);

			return;
		}

		$view = View :: getInstance();

		if ($message) {
			foreach ($message as $key => $value) {
				$view->$key = $value;
			}
		}
		$view->template(self :: $moduleName . '.html'); //default html

		$controller = new $controller();
		$template   = call_user_func([$controller, 'index']);
		unset($controller);

		if ($service === true) {
			$service = Service :: getInstance();
		}
		self :: closeConnections();
		//header(' ', true, 404);
		echo($view->render(false));
	}

	static function closeConnections() {
	}

	static function di(&$controller) {
		$controller->request = Request::getInstance();
		$controller->view    = View::getInstance();
		$controller->session = Session::getInstance();
	}

	/**
	 * Redirect or direct to a action or default controller action and parameters
	 * it has the ability to http redirect to the specified action
	 * internally used to direct to action.
	 *
	 * @param string $moduleName
	 * @param string $actionName
	 * @param array $parameters
	 * @param bool $httpRedirect
	 * @return bool
	 */
	static function redirect($moduleName , $actionName) {
		//die();
		self :: $moduleName = $moduleName;
		self :: $actionName = $actionName;
		//var_dump(DIR_APP . '/controller/' . $moduleName);
		//var_dump(is_dir(DIR_APP . '/controller/' . $moduleName));
		//die();
		if (is_dir(DIR_APP . '/controller/' . strtolower($moduleName))) {
			self :: $moduleName = $moduleName .= '/Index';
		}

		$dir = strtolower($moduleName);

		$controller = 'Vvveb\Controller\\' . str_replace('/', '\\',  $moduleName);
		//var_dump($controller);
		//var_dump($actionName);
		//die();

		//DIR_APP . '/controllers/' . self :: $app . '/' . $moduleName. '.php';
		//echo DIR_APP . '/controllers/' . PROJECT . '/' . self :: $app . '/' . $moduleName. '.php';
		//echo  DIR_APP . '/controller/' . $moduleName . '.inc';die();

		$file = DIR_APP . '/controller/' . $dir . '.php';

		if ((! @include_once(DIR_APP . '/controller/base.php')) ||
			(! file_exists($file) || ! @include_once(DIR_APP . '/controller/' . $dir . '.php'))) {
			$message = [
				'message' => 'Controller file not found!',
				'file'    => $file,
			];

			return self :: notFound(false, 404, $message);
		}

		// We check if the controller's class really exists
		if (class_exists($controller , false)) {// if the controller does not exist route to controller main
			$controller = new $controller();

			if (! $controller || ! method_exists($controller , $actionName)) {
				$message = [
					'message' => 'Method does not exist!',
					'file'    => $file,
				];

				return self :: notFound(false, 404, $message);
			}
		} else {
			$message = [
				'message' => 'Controller does not exist!',
				'file'    => $file,
			];

			return self :: notFound(false, 404, $message);
		}

		self :: di($controller);

		if (method_exists($controller, '_init')) {
			$controller->_init();
		}

		//$controller->db = $db;
		$template = strtolower(self :: $moduleName);
		$path     = DIR_WEBROOT . \Vvveb\config(APP . '.theme', 'default') . '/';

		if ($actionName && $actionName != 'index') {
			$html = $path . $template . '/' . strtolower($actionName) . '.html';

			if (file_exists($html)) {
				$template .= '/' . strtolower($actionName);
			}
		}

		$controller->view->template($template . '.html'); //default html
		$template = call_user_func([$controller, $actionName]);

		if ($template === false) {
			$controller->view->template(false);
		} else {
			if (is_array($template)) {
				echo json_encode($template);
			} else {
				if ($template) {
					$controller->view->template($template . '.html'); //default html
				}
			}
		}

		self :: closeConnections();
		$controller->view = view :: getInstance();

		//render template
		return $controller->view->render();
	}

	static public function dispatch() {
		//if host does not exist then 404
		//if (!is_dir(DIR_WEBROOT)) return FrontController::notFound(false);

		$_REQUEST = array_merge($_GET, $_REQUEST);

		//remove GET parameters to allow correct matching,
		$uri = preg_replace('/\?.*$/', '', $_SERVER['REQUEST_URI']);

		if (! isset($_GET['module']) && $parameters = Routes::match($uri)) {
			$_GET = array_merge($parameters, $_GET);
		}

		if (isset($_GET['route'])) {
			if (preg_match('@(^.+?)/(\w+$)@', $_GET['route'], $routeMatch)) {
				$_GET['module'] = $routeMatch[1];
				$_GET['action'] = $routeMatch[2];
			} else {
				$_GET['module'] = trim($_GET['route'], '/');
			}
		}

		if (! isset($_GET['module']) && isset($_POST['module'])) {
			$_GET['module'] = $_POST['module'];
		}

		if (! isset($_GET['action']) && isset($_POST['action'])) {
			$_GET['action'] = $_POST['action'];
		}

		if (! isset($_GET['module'])) {//if no controller provided, set main controller
			$_GET['module'] = 'index';
		}

		if (empty($_GET['action'])) {//if no action provided, set index action
			$_GET['action'] = 'index';
		}

		//santize inputs
		if (! preg_match('@^[a-zA-Z_/0-9]{4,30}$@', $_GET['module']) || ! preg_match('@^[a-zA-Z_/0-9]{3,30}$@', $_GET['action'])) {
			die('preg');

			return FrontController::notFound();
		}
		$path = $_GET['module'];

		$_GET['module'] = ucfirst($_GET['module']);
		$path           = '';

		array_map(function ($value) use (&$path) {
			if ($path) {
				$path .= '/';
			}
			$path .= ucfirst($value);
		}, explode('/', $_GET['module']));

		return self :: redirect($path, $_GET['action']);
	}
}
