<?php

// The basic Controller.

use LightnCandy\LightnCandy;

class ModuleMain {

	use \modules\output\traits {
			\modules\output\traits::__construct as __ConstructOutput;
		}

	protected $module_dir;
	public $settings;
	public $CONFIG;
	public $editHash;

	// parameter required if loadModel or __loadView is being used
	public function __construct($dir = '') {
		$this->__ConstructOutput();
		if (empty($dir)) $dir = __DIR__ ;
		$this->module_dir = $dir;
		$GLOBALS['loader']->UserId(isset($_SESSION['current_user']) ? (int) $_SESSION['current_user']['u_id'] : 0);
	}

	static 
	public function GetHandle() {
		$name = static::class;
		if (!isset($GLOBALS['handle'][$name])) {
			return $GLOBALS['handle'][$name] = new static;
		} else {
			return $GLOBALS['handle'][$name];
		}
	}

	public function loadModel($name, $module = '') {
		if ($module == '') {
			$dir = empty($this->module_dir) ? HOME : $this->module_dir;
		} else {
			$dir = HOME . '/modules/' . $module;
		}
		$file = $dir .'/model/'. strtolower($name) .'.php';
		$classname = '';
		if (file_exists($file)) {
			try {
				require_once $file;
			} catch (\Throwable $e) {			// php 7
				die('Error:' . $e->getMessage() . ' in ' . $e->getFile() . ' at line ' . $e->getLine() . ' [2]');
			}
			$info = $GLOBALS['loader']->GetFileInformation($file);
			foreach($info as $info_classname => $info_item) {
				$x = isset($info_item['.ext']) ? strtolower($info_item['.ext']) : '';	// extends
				if (strpos($x, 'model') !== FALSE || strpos($x, '_base') !== FALSE) {
					$classname = $info_classname;
					break;
				}
                if (strpos($info_classname, '_model') !== FALSE) {
					$classname = $info_classname;
					break;
                }
			}
		} else {
			throw new \Exception("loadModel: missing file $file");
		}
		if ($classname != '') {
			try {
				if (method_exists($classname, 'GetHandle')) {
					$model = $classname::GetHandle();
				} else {
					$model = new $classname;
				}
			} catch (\Throwable $e) {			// php 7
				die('Error:' . $e->getMessage() . ' in ' . $e->getFile() . ' at line ' . $e->getLine() . ' [2]');
			}
			$model->controller = $this;
		} else {
			throw new Exception("loadModel: unable to determine classname - $file");
//			echo "Modulemain:loadmodel "; var_dump($info);
			$model = FALSE;
		}
		return $model;
	}

	public function __loadView($name) {
		$dir = empty($this->module_dir) ? HOME : $this->module_dir;
		$file = $dir .'/view/'. strtolower($name) .'.php';
		try {
			require_once $file;
		} catch (Throwable $e) {			// php 7
			die('Error:' . $e->getMessage() . ' in ' . $e->getFile() . ' at line ' . $e->getLine() . ' [3]');
		}
		$view = new $name;
		$view->static = '';
		// static folder present?  Load in uri
		if (!empty($this->module_dir)) {
			$modulename = basename($this->module_dir);
			list($modname, $info) = $GLOBALS['loader']->locateModule($modulename);
			if ($info !== FALSE && isset($info['static'])) {
				$view->static = $info['static'];
			}
		}
		return $view;
	}
	
	public function GetModuleSettings() {
		return $GLOBALS['loader']->LoadModuleSettings($this->module_dir);
	}

	public function GetModuleName($key = FALSE) {
		$modulename = $GLOBALS['loader']->getModuleName($this->module_dir);
		if ($key && $modulename != '') $modulename = 'M:' . md5($modulename);		// this is used in user_data table
		return $modulename;
	}
	
	protected function autoloadRegister($source_component, $target) {
		if ($target == '.') $target = $this->module_dir;
		spl_autoload_register(function($class) use ($source_component, $target) {
			$sl = strlen($source_component);
			if (substr($class, 0, $sl) == $source_component) {
				$class = str_replace('\\', '/', substr($class, $sl));
				$filename = $target . $class . '.php';
				if (file_exists($filename)) {
					try {
						require_once $filename;
					} catch (Throwable $e) {			// php 7
						die('Error:' . $e->getMessage() . ' in ' . $e->getFile() . ' at line ' . $e->getLine() . ' [15]');
					}
					return TRUE;
				}
			}
			return FALSE;
		});
	}
	
	public function Activate() {
	}
	
	public function LoadHandlebarsView($viewfile) {
		// viewfile is classname::functionname
		[ $classname, $functionname ] = explode('::', $viewfile);
		// module name is taken from $module_dir in the contructor
		
		// load the view
		$view = $this->__loadView($classname);
		// execute the template function
		$res = call_user_func([$view, $functionname]);		// this view function MUST return a fileobject
		$template = $res->Html();
		return $this->LoadHandlebars($template);
	}
	
	// https://github.com/zordius/lightncandy
	public function LoadHandlebars($template) {

		if (!class_exists('LightnCandy\LightnCandy', FALSE)) {
			require_once __DIR__  . '/lightncandy/loader.php';
		}

		// if the template contains macros, import those 
		$param = [
			'helpers' => [
				'ifeq'   =>	function ($arg1, $arg2, $options) {
								if ($arg1 == $arg2) {
									return $options['fn']();
								} else {
									return $options['inverse']();
								}
							},
				'indirect' => function ($root, $fieldname, $options) {
								$node = $root[$fieldname] ?? '';
								return $node;
							},
				'ifindirect'   =>	function ($root, $fieldname, $options) {
								$node = $root[$fieldname] ?? 0;
								if ($node) {
									return $options['fn']();
								} else {
									return $options['inverse']();
								}
							},
			],
			'flags' => LightnCandy::FLAG_HANDLEBARS | LightnCandy::FLAG_BESTPERFORMANCE | LightnCandy::FLAG_RUNTIMEPARTIAL | LightnCandy::FLAG_ERROR_EXCEPTION,
			'partials' => [],
		];

		// get all macros (partials)
		$list = $this->GetInclude(FALSE);
		foreach($list as $includeitem) {
			if ($includeitem['macro'] !== FALSE) {
				$param['partials'][$includeitem['macro']] = trim($includeitem['content']);
			}
		}

		// TO FIX: md5 hash should also be based off helpers and partials
		
		// md5 is used so we only compile when the source template has changed
		$dir = HOME . '/storage/handlebars';
		$workfile = $dir . '/hb' . md5($template) . '.php';
		if (!file_exists($workfile)) {		// do we have a compiled copy? 

			$php = LightnCandy::compile($template, $param);
			file_put_contents($workfile, '<?php ' . $php);

		}
		// return the compiled function
		$renderer = include($workfile);		// get a handle to the compiled function
		return $renderer;
	}
	
}

