<?php defined('SYSPATH') or die('No direct access allowed.');

/**
 * This class overrides Kohana's ORM
 *
 * This file contains enhancements for Kohana, that should be considered upstream and maybe havent been yet.
 * It also contains some functionality for OSB, which cannot be covered in ORM_OSB.
 *
 * @package    OSB
 * @category   Modifications
 * @author     Deon George
 * @copyright  (c) 2009-2013 Open Source Billing
 * @license    http://dev.osbill.net/license.html
 */
abstract class ORM extends Kohana_ORM {
	protected $_table_names_plural = FALSE;
	protected $_model_names_plural = FALSE;
	private $_object_formated = array();
	private $_formated = FALSE;
	// Our filters used to display values in a friendly format
	protected $_display_filters = array();
	// Tables that do not have a site_id column
	public static $no_site_id_tables = array('setup','country','currency','language','tax');

	/**
	 * Add our OSB site_id to each SELECT query
	 * @see parent::__build()
	 */
	final protected function _build($type) {
		// Exclude tables without site ID's
		if (! in_array($this->_table_name,ORM::$no_site_id_tables))
			$this->where($this->_object_name.'.site_id','=',Company::instance()->site());

		// Ensure we Cache our queries
		$caching = FALSE;
		foreach ($this->_db_pending as $method)
			if ($method['name'] == 'cached') {
				$caching = TRUE;
				break;
			}

		if (! $caching)
			$this->cached(Kohana::$config->load('cache.orm.'.$this->_table_name));

		return parent::_build($type);
	}

	/**
	 * Format fields for display purposes
	 *
	 * @param string column name
	 * @return mixed
	 */
	private function _format() {
		foreach ($this->_display_filters as $column => $formats)
			$this->_object_formated[$column] = $this->run_filter($column,$this->__get($column),array($column=>$formats));

		$this->_formated = TRUE;
	}

	/**
	 * Function help to find records that are active
	 */
	protected function _where_active() {
		return $this->where('status','=',TRUE);
	}

	/**
	 * Determine if the account is authoised by the user
	 */
	public function authorised(Model $o=NULL,Model_Account $ao=NULL,$aid='account_id') {
		if (is_null($o))
			$o = $this;
		if (is_null($ao))
			$ao = Auth::instance()->get_user();

		return in_array($o->{$aid},$ao->RTM->customers($ao->RTM));
	}

	/**
	 * Overrides Kohana cache so that it can be globally disabled.
	 */
	public function cached($lifetime=NULL) {
		return $this->_db->caching() ? parent::cached($lifetime) : $this;
	}

	public function clear() {
		$this->_formated = FALSE;
		$this->_object_formated = array();

		return parent::clear();
	}

	/**
	 * Override KH's ORM count_relations() function, to include our site_id in the query.
	 *
	 * This is a copy of KH's ORM count_relations() function, with the addition of a where
	 * clause to include the site id.
	 */
	public function count_relations($alias, $far_keys = NULL)
	{
		if ($far_keys === NULL)
		{
			return (int) DB::select(array(DB::expr('COUNT(*)'), 'records_found'))
				->from($this->_has_many[$alias]['through'])
				->where($this->_has_many[$alias]['foreign_key'], '=', $this->pk())
				->where('site_id', '=', Company::instance()->site())
				->execute($this->_db)->get('records_found');
		}

		$far_keys = ($far_keys instanceof ORM) ? $far_keys->pk() : $far_keys;

		// We need an array to simplify the logic
		$far_keys = (array) $far_keys;

		// Nothing to check if the model isn't loaded or we don't have any far_keys
		if ( ! $far_keys OR ! $this->_loaded)
			return 0;

		$count = (int) DB::select(array(DB::expr('COUNT(*)'), 'records_found'))
			->from($this->_has_many[$alias]['through'])
			->where($this->_has_many[$alias]['foreign_key'], '=', $this->pk())
			->where($this->_has_many[$alias]['far_key'], 'IN', $far_keys)
			->where('site_id', '=', Company::instance()->site())
			->execute($this->_db)->get('records_found');

		// Rows found need to match the rows searched
		return (int) $count;
	}

	/**
	 * Return a formated columns, as per the model definition
	 */
	public function display($column) {
		// Trigger a load of the record.
		$value = $this->__get($column);

		// If some of our fields need to be formated for display purposes.
		if (! $this->_formated AND $this->_display_filters)
			$this->_format();

		if (isset($this->_object_formated[$column]))
			return $this->_object_formated[$column];
		else
			return $value;
	}

	public function display_filters(array $filters) {
		$this->_display_filters = Arr::merge($this->_display_filters,$filters);
	}

	/**
	 * Function help to find records that are active
	 */
	public function list_active() {
		return $this->_where_active()->find_all();
	}

	/**
	 * This function is our AJAX helper, used by module list_autocomplete()
	 */
	public function list_autocomplete($term,$index,$value,array $label,array $limit=array(),array $options=NULL) {
		$result = array();

		$query = empty($options['object']) ? $this : $options['object'];

		foreach ($limit as $w) {
			list($k,$s,$v) = $w;

			$query->and_where($k,$s,$v);
		}

		$c = 0;
		foreach ((empty($options['object']) ? $query->find_all() : $query->execute()) as $o) {
			// If we got here via a DB query, we need to reload our ORM object from the result.
			if (! is_object($o)) {
				if (empty($options['key']))
					throw new Kohana_Exception('Missing key for non object');

				$o = $this->clear()->where($options['key'],'=',$o[$options['key']])->find();
			}

			switch ($index) {
				case 'url':
					if (empty($options['urlprefix']))
						throw new Kohana_Exception('Missing URL Prefix');

					$v = $options['urlprefix'].$o->resolve($value);

					break;

				default: $v = $o->resolve($value);
			}

			$k = '';
			foreach ($label as $k => $details)
				foreach ($details as $lvalue)
					$k = preg_replace('/%s/',$o->resolve($lvalue),$k,1);

			$result[$c++] = array(
				'value'=>$v,
				'label'=>$k,
			);
		}

		return $result;
	}

	/**
	 * This function is used so that methods can be called via variables
	 */
	public function resolve($key) {
		eval("\$x = \$this->$key;");

		return $x;
	}

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

	public function where_authorised(Model_Account $ao=NULL,$aid='account_id') {
		if (is_null($ao))
			$ao = Auth::instance()->get_user();

		return $this->where($aid,'IN',$ao->RTM->customers($ao->RTM));
	}
}
?>