<?php

// get list of all compilable views - use the register function

class Handlebars_Model extends Model {
	
	// load in the javascript file (if present) to get existing names and md5s
	
	// load the controllers of every active module
	// run the register function 
	//  	error on duplicate names
	// store name and md5 if changed from the js file
	const JSFILE = 'handlebars-data.js';
	private $jsdata;

	public function __construct() {
		parent::__construct();
		$this->load_js();
	}
	
	public function Enumerate($essential = 0) {
		$data = $GLOBALS['loader']->getModuleList();
		$modules = array_keys($data);
//		var_dump($modules);

		$old_views = $GLOBALS['loader']->VIEWS;
		$old_level = $GLOBALS['loader']->UserLevel();
		$GLOBALS['loader']->VIEWS = [];
		$GLOBALS['loader']->UserLevel(99);
		foreach($modules as $module) {
			if ($GLOBALS['loader']->isModuleActive($module)) {
				$this->processModule($module);
			}
		}
		// get only handlebars views - save name and hash
		$results = [];
		foreach($GLOBALS['loader']->VIEWS as $filename => $viewdata) {
			$view = $viewdata['*'];
			$views = $view->GetInclude(FALSE);
			foreach($views as $result) {
				if ($result['type'] == 'handlebars') {
					$md5 = $result['md5'];
					if (isset($results[$result['name']]) && $results[$result['name']]['md5'] != $md5) {
						die("the name \"{$result['name']}\" is not unique\n");
					}
					if ($result['macro'] === FALSE) {		// macros are not included
						$payload = md5($md5 . ':' . $result['name']);
						$results[$result['name']] = [
							'name'		=> $result['name'],
							'md5' 		=> $md5,
							'auth'		=> $auth = hook_execute('nonce.create', FALSE, 'handlebar', $payload, 300),
						];
					}
				}
			}
		}
		
		if ($essential) {
			$keep = ['admin_index', 'admin_handlebars', 'setup_index', 'setup_system', 'setup_keyinfo', 'setup_select', 'setup_module', 'setup_modules', 'admin_email', 'account_invite', 'account_new', 'account_invitepop', 'account_edit', 'account_view'];
			foreach($results as $name => $itemdata) {
				if (!in_array($name, $keep)) {
					unset($results[$name]);
					$this->_enumerateRemoveView($name);	// remove from view - no need to include it on the webpage
				}
			}
		} else {
			foreach($this->jsdata as $name => $itemdata) {
				if ($itemdata['hash'] == $results[$name]['md5']) {
					unset($results[$name]);				// nothing has changed - no update required
				}
				if ($name != 'admin_handlebars') {
					$this->_enumerateRemoveView($name);	// remove from view - no need to include it on the webpage
				}
			}
		}
		
		// remove unused templates from js
		$GLOBALS['loader']->UserLevel($old_level);
		return array_values($results);
	}
	
	private function _enumerateRemoveView($name) {
		foreach($GLOBALS['loader']->VIEWS as $filename => $viewdata) {
			$view = $viewdata['*'];
			$views = $view->GetInclude(FALSE);
			foreach($views as $result) {
				if ($result['type'] == 'handlebars' && $result['name'] == $name) {
					$view->RemoveItem($name);
					break;
				}
			}
		}
	}
	
	protected function processModule($name) {
		// load the view
		$GLOBALS['loader']->LoadModule($name, TRUE);
		// get all controllers
		$controllers = $GLOBALS['loader']->ProcessControllers($name);
		foreach($controllers as $controllername => $controllerinfo) {
			$this->processController($name, $controllername, $controllerinfo);
		}
	}
	
	private function processController($module, $controllername, $controllerinfo) {
		// load the controller file
		foreach($controllerinfo as $classname => $classinfo) {
			if (isset($classinfo['__register'])) {
				$GLOBALS['loader']->executeClassMethod($controllername, $module, $classname, '__register');
			}
		}
	}
	
	protected function load_js() {
		$filename = HOME . '/storage/' . static::JSFILE;
		if (file_exists($filename)) {
			$contents = file_get_contents($filename);
			$text = preg_match('~\{(.*)\}~', $contents, $match) ? $match[0] : '';
			$this->jsdata = json_decode($text, TRUE);
		} else {
			$this->jsdata = [];
		}
	}
	
	protected function update_js($name, $md5, $content) {
		$this->jsdata[$name] = [
			'hash' 	  => $md5,
			'content' => $content,
			'date'	  => time(),
		];
		asort($this->jsdata);
	}
	
	protected function save_js() {
		$filename = HOME . '/storage/' . static::JSFILE;
		$output = "window.HANDLEBAR = " . json_encode($this->jsdata, JSON_UNESCAPED_SLASHES) . ';';
		file_put_contents($filename, $output);
	}
	
	public function updateHandlebar($payload) {
		$hash = $payload['hash'];		// this is the crc of the original html returned from the view
		$name = $payload['name'];
		$auth = hook_execute('nonce.verify', FALSE, 'handlebar', FALSE, $payload['auth']);
		if ($auth === FALSE) {
			return [
				'result' => 5,
				'message' => 'Access to this handlebar function is denied',
			];
		}
		$desired = md5($hash . ':' . $name);
		if ($auth !== $desired) {
			return [
				'result' => 401,
				'message' => "You've done something sneaky.",
			];
		}

		//$this->load_js();
		$this->update_js($name, $hash, $payload['content']);
		// update the entry
		$this->save_js();
		
		return [
			'result' => 0,
		];
	}
	
	public function __toString() {
		$filename = HOME . '/storage/' . static::JSFILE;
		if (file_exists($filename)) {
			return file_get_contents($filename);
		}
		return '';
	}
	
	public function getFileVersion() {
		$filename = HOME . '/storage/' . static::JSFILE;
		if (file_exists($filename)) {
			return md5_file($filename);
		} else {
			return md5('');
		}
	}
	
	// remove any handlebars that are the same version as in the js file
	public function updateIncludes(&$includes) {
		//var_dump($includes);
		foreach($includes as $key => $item) {
			if ($item->ext() == 'handlebars') {
				$md5  = $item->md5;
				$current_md5 = $this->jsdata[$key]['hash'] ?? '';
				if ($md5 == $current_md5) {
					unset($includes[$key]);
				}
			}
		}
		if (0 && $_SERVER['REMOTE_ADDR'] == '173.48.93.56') {
			header('Content-type: text/plain');
			var_dump($includes);
			exit;
		}
	}
	
}