<?php 

namespace Tools;

/**
 * Container all modules.
 * Allow module managment
**/
class ModuleManager
{
    /**
     * @var \Tools\Context $context
     * /core/tools/Context.php
     * Contains website's informations
    **/
    private $context;

    /**
     * @var array(\Tool\AModule) $modules
     * /core/tools/AModule.php
     * Contains all loaded modules
     * Can be accessed read-only via $instance->modules
    **/
    private $modules = array();

	/**
	 * @var boolean $installing true if a module is currently installing
	**/
	private static $installing = false;

    /**
     * @var _id_{ID}
     * Get the module identified with id ID
     * ex: $context->moduleManager->_id_2 will return module with id 2
    **/

    /**
     * @param \Tool\Context $context
     * Load all active modules from database.
     * Enable hooks for these modules
     * Disable them if the module cannot be loaded
    **/
    public function __construct(&$context)
    {
        $this->context = $context;
        $modulesRoot = $context->router->modulesPath;
        $modules = \Entity\Module::getActivated();
        $ids = array();
        foreach ($modules as $i)
        {
            $modulePath = "{$modulesRoot}{$i->directory}/main.php";
            if (file_exists($modulePath) && $this->loadModule($modulePath, $i))
            {
                $ids[] = $i->id;
            }
            else
            {
                $i->active = false;
                $i->save();
            }
        }
        $context->hookManager->loadHooks(\Entity\ModuleHook::getModules($ids));
    }

	/**
	 * @param string $path
	 * @return \Tools\AModule loaded module (or false on error)
	**/
	private function loadModuleFile($path)
	{
        $mod = include_once($path);
        if (!$mod || !($mod instanceof \Tools\AModule))
            return false;
        $mod->setContext($this->context);
		return $mod;
	}

    /**
     * @param string $path path to module's main file
     * @param \Entity\Module $module module's database object to load
     * /core/models/Module.php
     * @return TRUE on success
     * Will try to load module located at $path.
     * This function will include the main.php file located in the module's directory
     * The file MUST return an AModule object to be considered as successfull
     * /core/tools/AModule.php
    **/
    private function loadModule($path, $module)
    {
		$mod = $this->loadModuleFile($path);
		if ($mod === false)
			return false;
        $mod->setEntity($module);
        $this->modules[] = $mod;
        return true;
    }

	/**
	 * @param string $modulename
	 * @return \tools\AModule|false on failure
	 * Install the module located in {modulePath}/modulename/modulename.php
	**/
	public function install($modulename)
	{
		$entity = new \Entity\Module();
		$entity->name = $modulename;
		$entity->directory = $modulename;
		$entity->active = true;
		$module = $this->loadModuleFile($this->context->router->modulesPath.$modulename.'/main.php');
		if ($module === false)
			return false;
		$module->setEntity($entity);
		$entity->name = $module->getName();
		$entity->description = $module->getDescription();
		$entity->save();
		self::$installing = true;
		if ($module->install() == false)
		{
			self::$installing = false;
			$hooks = \Entity\ModuleHook::getModules($entity->id);
			foreach ($hooks as $i)
				$i->delete();
			$entity->delete();
			return false;
		}
		self::$installing = false;
		return $module;
	}

    /*
     * TODO revoir tout
     * @return array(AModule)
     * Will load every modules, and return them.
    **/
    public function listAvailableModules()
    {
        $modulesRoot = $context->router->modulesPath;
        $result = array();
        $modules = scandir($modulesRoot, SCANDIR_SORT_NONE);
        foreach ($modules as $i)
        {
            $path = $modulesRoot.$i;
            if ($i == '.' || $i == '..' || !is_dir($path))
                continue;
            $this->loadModule($path);
        }
    }

    /**
     * @param int $id
     * @return AModule on success, FALSE on failure (no such ID / module not loaded)
     * Get the module identified with id ID
    **/
    public function getModuleFromId($id)
    {
        foreach ($this->modules as $i)
        {
            if ($i->entity->id == $id)
                return $i;
        }
        return FALSE;
    }

	/**
	 * @return true if a module is currently installing
	**/
	public static function isInstalling()
	{ return self::$installing; }

	/**
	 * Getter
	**/
    public function __get($key)
    {
        switch ($key)
        {
            case "modules":
                return $this->modules; break;
        }
        if (substr($key, 0, 4) == "_id_")
            return $this->getModuleFromId((int) substr($key, 4));
        throw new \Exception("Cannot access attribute {$key}");
    }
}

