<?php

namespace modules\database;

class main extends \moduleMain {

	private $engines = [];
	private $cache;
	private $cache2;

	use \modules\config\traits {
			\modules\config\traits::__construct as __ConstructConfig;
		}
	use \modules\database\traits {
			\modules\database\traits::__construct as __ConstructDatabase;
		}
	
	public function __construct() {
		parent::__construct(__DIR__);
		$this->__ConstructConfig();
		$this->__ConstructDatabase();
		$this->Enumerate();
	}

	public function Activate() {
//		echo '<pre>' . htmlspecialchars(var_export(debug_backtrace(), 1)) . '</pre>';
		hook_add('menu.admin', [$this, '__admin_menu'], 94);
		hook_add('db.load', [$this, '__load'], 50);			// get pointer to specific database engine
		hook_add('db.migrate', [$this, '__migrate'], 50);	// upgrade database (creates missing tables; updates changed definitions)
		hook_add('pkp.setup', [$this, '__migrate'], 2);		// initial setup of database tabkes
		hook_add('uri', [$this, '__check'], 1);	// do we have any db engines?
		hook_add('config.set', [$this, '__setconfigitem']);
		hook_add('config.get', [$this, '__getconfigitem']);			// database handler for config options
		$this->_loadCache();
	}

	public function __admin_menu(&$menu) {
		$menu[] = [
				'caption' => 'Database Migration', 
				'href' => '/admin/migrate', 
				'hint' => 'Perform a Database Migration to upgrade fields in the database',
				'icon' => '<i class="fas fa-database"></i>',
				's' => 0
			];
	}
	
	public function __check(&$uri, $__INPUT, $__OUTPUT) {
		if (!count($this->engines)) {		// if no engines, and not on error page, redirect to error page
			$status = new \http_status(460);
			$status->Message('No suitable database engine Found')		// debian: apt install  php7.0-mysql
					->Note('Please install mysqli php module');
			$__OUTPUT->HttpStatus($status); 
			$uri = FALSE;
		}
	}
	
	public function Enumerate() {
		$mask = __DIR__ . '/model/engine_*.php';
		$filelist = glob($mask);
		foreach($filelist as $filename) {
			$info = $GLOBALS['loader']->GetFileInformation($filename);
			$class = '';
			foreach($info as $class_name => $class_info) {
				if (strpos(strtolower($class_name), 'engine') !== FALSE) {
					$class = $class_name;
					break;
				}
			}
			if ($class != '') {
				require_once $filename;
				$reflection = new \ReflectionClass($class);
				$constants = $reflection->getConstants();
				if (isset($constants['LABEL'])) {
					$engine = new $class;
					if ($engine->is_installed()) {
						$this->engines[$constants['LABEL']] = $engine;
					}
				}
			}
		}
	}
	
	// to do: allow other engines
	private function _getconnectionString($requested_engine) {
		if ($requested_engine == 'mysql') {
			$db_host = $this->SettingGet('db_host', 'localhost');
			$db_name = $this->SettingGet('db_name', '');
			$db_user = $this->SettingGet('db_user', '');
			$db_pass = $this->SettingGet('db_pass', '');	// base64 encoded
			return 'mysql://' . $db_user . ':' . $db_pass . '@' . $db_host . '/' . $db_name;
		}
	}
	public function __load(&$engine, $requested_engine, $connection_string = '') {
		if (empty($engine)) {
			if ($requested_engine == 'auto') {
				if (isset($this->engines['mysql'])) {
					$requested_engine = 'mysql';
				}
			} else {
				reset($this->engines);
				$requested_engine = key($this->engines);
			}
			
			if (isset($this->engines[$requested_engine])) {
				$engine = $this->engines[$requested_engine];
			}
			if (empty($engine)) return;		// no engine. do not try to connect
					
			if (empty($connection_string)) {
				$connection_string = $this->_getconnectionString($requested_engine);
			}
			$j = $engine->connection();
			if (empty($j)) $engine->connection($connection_string);
		}
	}
	
	private function _loadCache() {
		$hashes = [];
		$modules = $GLOBALS['loader']->getModuleList();
		foreach($modules as $module_name => $module_info) {
			$hash = 'M:' . md5($module_name);
			$hashes[$hash] = $module_name;
		}
		$data = $this->tablename('config')
					 ->Query('select cfg_hash,cfg_text from `config`')
					 ->where('cfg_hash like %s', 'M:%')
					 ->get();
        if (!is_null($data)) {
            foreach($data as $data_item) {
                $key = $data_item->cfg_hash;
                if (isset($hashes[$key])) {
                    $module_name = $hashes[$key];
					if (is_null($data_item->cfg_text)) {
						$this->cache2[$module_name] = NULL;
					} else {
//						error_log('loadCache: ' . $module_name . ' ' . $key);
						$this->cache2[$module_name] = unserialize($data_item->cfg_text);
					}
                }
            }
        }
		foreach($modules as $module_name => $module_info) {
			if (!isset($this->cache2[$module_name])) $this->cache2[$module_name] = [];
		}
	}
	
	// this will only pick up global settings for the module.  user-based are loaded from account module
	public function __getconfigitem(&$result, $module_name) {
		if (!isset($this->cache2[$module_name])) {
			$hash = 'M:' . md5($module_name);
			$item = $this->Query('select cfg_hash,cfg_text from `config`')->where('cfg_hash=%s', $hash)->First();
			if (!is_object($item)) {
				$payload = [];
			} elseif (!$item->empty()) {
				$payload = unserialize($item->cfg_text);
			} else {
				$payload = [];
				// get the original v1 record (temp), save as v2
/*				$hash2= 'module:' .md5(strtolower($module_name . '_module_info'));
				$item = $this->Query('select cfg_hash,cfg_text from `config`')->where('cfg_hash=%s', $hash2)->First();
				if (!$item->empty()) {
					$payload = unserialize($item->cfg_text);
					$this->tablename('config')->InsertOrUpdate([
						'cfg_hash' => $hash
					], [
						'cfg_text' => $item->cfg_text
					]); 
				} */
			}
			$this->cache2[$module_name] = $payload;
		}
		$module_info = $GLOBALS['loader']->GetModuleInfo($module_name);
        if (isset($module_info['.info']['settings']) && is_array($module_info['.info']['settings'])) {
            foreach($module_info['.info']['settings'] as $field_def) {
                $key = $field_def['name'];
                if (array_key_exists($key, $this->cache2[$module_name])) {
                    $result[$key] = $this->cache2[$module_name][$key];
                }
            }
        }
	}

	public function __setconfigitem(&$data, $module_name) {
		if (!is_array($data)) return;	// invalid, or already handled
	// save just the global elements
		$module_info = $GLOBALS['loader']->GetModuleInfo($module_name);
		$hash = 'M:' . md5($module_name);
		$payload = [];
		foreach($module_info['.info']['settings'] as $field_def) {
			$key = $field_def['name'];
			if (array_key_exists($key, $data)) {
				$payload[$key] = $data[$key];
			}
		}
		if (count($payload)) {
			$this->tablename('config');
			$this->InsertOrUpdate([
				'cfg_hash' => $hash
			], [
				'cfg_text' => serialize($payload)
			]);
			$this->cache2[$module_name] = $payload;
		}
	}
	
	public function __migrate(&$output, $controller) {
		require_once __DIR__ . '/model/upgrade_model.php';
//		echo '<pre>' . htmlspecialchars(var_export(debug_backtrace(), 1)) . '</pre>';
		$model = new \Upgrade_Model();
		$output .= $model->TableMigration();
	}

}

require_once __DIR__ . '/reportobject.php';