<?php
class ModuleInstance extends MY_Model
{
	const DB_TABLE = 'module_instances';
    const DB_TABLE_PK = 'module_instance_id';

	public $messages 			= array();
	public $errors 				= array();

	protected $enable_jquery 	= false;
	protected $enable_jquery_ui = false;
	protected $children 		= array();
	protected $module_settings 	= array();
	protected $scripts 			= array();
	protected $cache_config 	= array();

	public function __construct()
	{
		parent::__construct();

		$this->sort         = 1;
		$this->published    = 1;
		$this->cache_config = (object)array("enabled" => true, "cache_time" => 86400, "user" => false, "url" => false, "query_string" => false);
	}

	// Module settings are the same as the old module params but they are indexed by key rather than numerical.
	public function module_settings()
	{
		return $this->module_settings;
	}

	public function scripts()
	{
		return $this->scripts;
	}

	public function enable_jquery()
	{
		return $this->enable_jquery;
	}

	public function enable_jquery_ui()
	{
		return $this->enable_jquery_ui;
	}

	public function children()
	{
		return $this->children;
	}

	function load($id)
	{
		$key = "moduleinstance:load:" . $id;

		if(($query = $this->cache->get($key)) === false)
		{
			$query = $this->db->query("SELECT mi.*, m.`name`, m.`file_name`, m.`class_name`
			                          FROM module_instances AS mi
			                          INNER JOIN modules AS m ON mi.module_id=m.module_id
			                          WHERE mi.module_instance_id=?",
			                          array($id));
			$query = $query->row();
			$this->cache->save($key, $query, 86400);
		}

		$success = $this->populate($query);

		$this->load_settings();

		return $success;
	}

	function load_settings()
	{
		if(!isset($this->module_instance_id))
		{
			return false;
		}

		$this->load->model("modulesetting");

		$this->module_settings = $this->modulesetting->load_by_instance_id($this->module_instance_id);
	}

	function setting($key, $default="")
	{
		if(isset($this->module_settings["$key"]))
		{
		 	return $this->module_settings["$key"]->value;
		}
		else
		{
			return $default;
		}
	}

	/**
	 * Set a module setting value. This is mostly for QA tests since it does not actually save the value.
	 */
	function set($key, $value)
	{
		$this->module_settings["$key"] = new StdClass();
		$this->module_settings["$key"]->key = $key;
		$this->module_settings["$key"]->value = $value;
	}

	function LoadByPageID($id, $user)
	{
		$key = "modules:page:" . $id . ":" . $user->user_id;

		if(($query = $this->cache->get($key)) === false)
		{
			$query = $this->db->query("SELECT mi.*, m.`name`, m.`file_name`, m.`class_name`
						FROM module_instances AS mi
						INNER JOIN modules_pages AS mp ON mi.module_instance_id=mp.module_instance_id
						INNER JOIN modules AS m ON mi.module_id=m.module_id
						INNER JOIN `acl` AS a ON mi.module_instance_id=a.module_instance_id
						INNER JOIN pages AS p ON mp.page_id=p.page_id
						WHERE p.page_id=? AND mi.published='1' AND mi.site_id=p.site_id AND (a.read=1 AND a.acl_role_id IN ?)
						GROUP BY mi.module_instance_id
						ORDER BY mi.sort DESC, mp.sort DESC, mi.module_instance_id DESC",
						array($id, $user->roles));	//load in reverse order due to how they are inserted
			$query = $query->result();
			$this->cache->save($key, $query, 86400);
		}

		return $query;
	}

	function LoadBySiteID($site_id, $orderby, $user)
	{
		$key = "modules:site:" . $site_id . ":" . $user->user_id;

		if(($query = $this->cache->get($key)) === false)
		{
			$query = $this->db->query("SELECT mi.*, m.`name`, m.`file_name`, m.`class_name`
						FROM module_instances AS mi
						INNER JOIN modules AS m ON mi.module_id=m.module_id
						INNER JOIN `acl` AS a ON mi.module_instance_id=a.module_instance_id
						WHERE mi.site_id=? AND (a.read=1 AND a.acl_role_id IN ?)
						GROUP BY mi.module_instance_id
						ORDER BY $orderby",
						array($site_id, $user->roles));
			$query = $query->result();
			$this->cache->save($key, $query, 86400);
		}

		return $query;
	}

	function LoadInstalledByPageID($id, $site_id)
	{
		$query = $this->db->query("SELECT mi.*, m.`name`, m.`file_name`, m.`class_name`,
		                          IF(mp.module_page_id IS NULL, '0', '1') AS module_installed
								  FROM module_instances AS mi
								  INNER JOIN modules AS m ON mi.module_id=m.module_id
								  LEFT JOIN modules_pages AS mp ON mi.module_instance_id=mp.module_instance_id AND mp.page_id=?
								  WHERE mi.site_id=? AND mi.position<>'' AND mi.published='1'
								  GROUP BY mi.module_instance_id
								  ORDER BY mi.tag",
								  array($id, $site_id));

		return $query->result();
	}

	function loadDefaultModules($site_id)
	{
		// $query = $this->db->query("SELECT mi.module_instance_id
		// 						  FROM
		// 						  (SELECT COUNT(*) AS total FROM pages WHERE site_id='" . $this->db->escape_str($site_id) . "' AND published=1) AS pages
		// 						  INNER JOIN
		// 						  (SELECT mi.module_instance_id,
		//                           SUM(IF(mp.module_page_id IS NULL, '0', '1')) AS total
		// 						  FROM module_instances AS mi
		// 						  INNER JOIN modules_pages AS mp ON mi.module_instance_id=mp.module_instance_id
		// 						  INNER JOIN pages AS p ON mp.page_id=p.page_id
		// 						  WHERE p.published='1' AND mi.published='1' AND p.site_id='" . $this->db->escape_str($site_id) . "' AND mi.position<>''
		// 						  GROUP BY mi.module_instance_id) AS mi USING(total)");

		$query = $this->db->query("SELECT mi.module_instance_id
								  FROM module_instances AS mi
								  WHERE mi.published='1' AND mi.site_id=? AND mi.position<>'' AND (mi.always_on=1 OR mi.always_on=2)", array($site_id));

		return $query->result();
	}

	/**
	 * Since we have a couple public class variables that are NOT fields, we have to unset them before saving.
	 */
	public function save()
	{
		$temp_scripts 	= $this->scripts;
		unset($this->scripts);
		$saved 			= parent::save();
		$this->scripts 	= $temp_scripts;

		return $saved;
	}

    function search($q, $user)
	{
		if($q == null)
		{
			return array();
		}

		if(!isset($user->roles) || count($user->roles) == 0)
    	{
    		$user->roles = array(1); // Public
    	}

		$q = $this->db->escape_str($q);

		$query = $this->db->query("SELECT i.*, m.`name`
		                          FROM module_instances AS i
		                          INNER JOIN `acl` AS a USING(module_instance_id)
		                          INNER JOIN modules AS m ON i.module_id=m.module_id
		                          WHERE (a.read=1 AND a.acl_role_id IN ?) AND
			                          `tag` LIKE '%$q%' OR
		                              m.`name` LIKE '%$q%'
		                          GROUP BY i.module_instance_id
		                          ORDER BY `tag`",
		                          array($user->roles));

		return $query->result();
	}

	// This function can be overridden in order to install module dependencies.
	// It will be called upon saving the module.
	public function install()
	{
		return true;
	}

	// Attempt to install a module with default parameters, if it is not already installed.
	protected function install_module($file_name, $tag, $sort=1, $login=0, $settings=array())
	{
		// Check if this module exists yet.
		$query = $this->db->query("SELECT module_instance_id
		                          FROM module_instances
		                          WHERE `module_id`=(SELECT module_id FROM modules WHERE file_name=?) AND
				                 `tag`=? AND
				                 `site_id`=? AND
				                 `position` IS NULL", array($filename, $tag, Mainframe::active_site_id()));

		// If the module exists, grab its ID.
		if($query !== false && $query->num_rows() > 0)
		{
			$row 			= $query->row();
			$module_instance_id 	= $row->module_instance_id;
		}
		// Otherwise, create it and grab the ID.
		else
		{
			$sql = "INSERT INTO module_instances SET
					`module_id`=(SELECT module_id FROM modules WHERE file_name=?),
					`site_id`=?,
					`tag`=?, `position`=NULL,
					`sort`=?, `published`=1";
			$this->db->query($sql, array($filename,
			                            Mainframe::active_site_id(),
			                            $tag,
			                 			(is_numeric($sort) ? $sort : 1),
			                 			($login ? 1 : 0)));
			$module_instance_id = $this->db->insert_id();
		}

		// Ensure that each default setting exists.
		foreach($settings as $key => $value)
		{
			$query = $this->db->query("SELECT module_setting_id FROM module_settings
			                          WHERE module_instance_id=? AND `key`=?", array($module_instance_id, $key));

			if($query->num_rows() == 0)
			{
				$this->db->query("INSERT INTO module_settings
				                 SET module_instance_id=?, `key`=?, `value`=?", array($module_instance_id, $key, $value));
			}
		}

		return $module_instance_id;
	}

	// Find a module with the given type and tag for the current site.
	protected function get_module_id($file_name, $tag)
	{
		// Check if this module exists yet.
		$query = $this->db->query("SELECT module_instance_id FROM module_instances
		                          WHERE `module_id`=(SELECT module_id FROM modules WHERE file_name=?) AND
		                         `tag`=? AND
				                 `site_id`=? AND
				                 `position` IS NULL", array($file_name, $tag, Mainframe::site()->site_id));

		// If the module exists, grab its ID.
		if($query->num_rows() > 0)
		{
			$row = $query->row();
			return $row->module_instance_id;
		}

		return false;
	}

	public function allowCaching()
	{
		return $this->cache_config->enabled;
	}

	public function getCacheTime()
	{
		return $this->cache_config->cache_time;
	}

	public function getCacheKey()
    {
    	return "module:" . Mainframe::site()->site_id . ":" . Mainframe::page()->page_id . ":" .
    			($this->module_instance_id ? $this->module_instance_id : "-") . ":" .
    			($this->cache_config->user ? Mainframe::user()->user_id : "-") . ":" .
    			($this->cache_config->url ? str_replace("/", "~", current_url()) : "-") .
    			($this->cache_config->query_string && $_SERVER["QUERY_STRING"] ? "?" . $_SERVER["QUERY_STRING"] : "");
    }
}
