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

/**
 * This class extends Kohana's [ORM] class to create defaults for OSB.
 *
 * @package    OSB
 * @subpackage Core
 * @category   ORM
 * @author     Deon George
 * @copyright  (c) 2010 Open Source Billing
 * @license    http://dev.osbill.net/license.html
 */
abstract class ORMOSB extends ORM {
	/**
	 * @var string Database to connect to
	 */
	protected $_db = 'default';

	protected $_created_column = array('column'=>'date_orig','format'=>TRUE);
	protected $_updated_column = array('column'=>'date_last','format'=>TRUE);

	// Our attribute values that need to be stored as serialized
	protected $_serialize_column = array();

	// Our attributes used in forms.
	protected $_form = array();

	public function rules() {
		return array(
			'id'=>array(
				array('ORMOSB::get_next_id',array(':validation',':model',':field')),
			),
			'site_id'=>array(
				array('ORMOSB::set_site_id',array(':validation',':model',':field')),
			),
		);
	}

	/**
	 * This function will enhance the [Validate::filter], since it always passes
	 * the value as the first argument and sometimes functions need that to not
	 * be the first argument.
	 *
	 * Currently this implements:
	 *   [date()][date-ref]
	 *
	 * [date-ref]: http://www.php.net/date
	 *
	 * This function will throw an exception if called without a function
	 * defined.
	 *
	 * @param mixed $val Value to be processed
	 * @param string $func Name of function to call
	 * @param string $arg Other arguments for the function
	 * @todo This has probably changed in KH 3.1
	 */
	final public static function _filters($val,$func,$arg) {
		switch ($func) {
			case 'date':
				return date($arg,$val);
			default:
				throw new Exception(sprintf(_('Unknown function: %s (%s,%s)'),$func,$arg,$val));
		}
	}

	final public static function form($table,$blank=FALSE) {
		return ORM::factory($table)->formselect($blank);
	}

	/**
	 * Get Next record id
	 *
	 * @param array Validate object
	 * @param string Primary Key
	 */
	// @todo Do we need the $array?
	public static function get_next_id(Validation $array,$model,$field) {
		if (! is_null($model->$field))
			return TRUE;

		$model->_changed[$field] = $field;

		$ido = ORM::factory('module')
			->where('name','=',$model->_table_name)
			->find();

		if (! $ido->loaded())
			throw new Kohana_Exception('Problem getting record_id for :table',array(':table'=>$model->_table_name));

		$model->$field = $ido->record_id->next_id($ido->id);

		return TRUE;
	}

	// @todo Do we need the $array?
	public static function set_site_id(Validation $array,$model,$field) {
		if (! is_null($model->$field))
			return TRUE;

		$model->_changed[$field] = $field;
		$model->$field = Config::siteid();

		return TRUE;
	}

	public function __get($column) {
		if (array_key_exists($column,$this->_table_columns)) {

			// If the column is a blob, we'll decode it automatically
			if (
				$this->_table_columns[$column]['data_type'] == 'blob'
				AND ! is_null($this->_object[$column])
				AND ! isset($this->_changed[$column])
				AND (! isset($this->_table_columns[$column]['auto_convert']) OR ! $this->_table_columns[$column]['auto_convert'])
			) {

				// In case our blob hasnt been saved as one.
				try {
					 $this->_object[$column] = $this->blob($this->_object[$column]);
				}
				catch(Exception $e) {
					// @todo Log this exception
					echo Kohana_Exception::text($e), "\n";
					echo debug_print_backtrace();
				}

				$this->_table_columns[$column]['auto_convert'] = TRUE;
			}

			// If the column is a serialized object, we'll unserialize it.
			if (
				in_array($column,$this->_serialize_column)
				AND is_string($this->_object[$column])
				AND ! is_null($this->_object[$column])
				AND ! isset($this->_changed[$column])
				AND (! isset($this->_table_columns[$column]['unserialized']) OR ! $this->_table_columns[$column]['unserialized'])
			) {

				// In case our object hasnt been saved as serialized.
				try {
					$this->_object[$column] = unserialize($this->_object[$column]);
				}
				catch(Exception $e) {
					// @todo Log this exception
					echo Kohana_Exception::text($e), "\n";
					echo debug_print_backtrace();
				}

				$this->_table_columns[$column]['unserialized'] = TRUE;
			}
		}

		return parent::__get($column);
	}

	public function formselect($blank) {
		$result = array();

		if ($blank)
			$result[] = '';

		foreach ($this->find_all() as $o)
			$result[$o->{$this->_form['id']}] = $o->{$this->_form['value']};

		return $result;
	}

	public function keyget($column,$key=NULL) {
		if (is_null($key) OR ! is_array($this->$column))
			return $this->$column;
		else
			return array_key_exists($key,$this->$column) ? $this->{$column}[$key] : NULL;
	}

	public function save(Validation $validation = NULL) {
		// Find any fields that have changed, and process them.
		if ($this->_changed)
			foreach ($this->_changed as $c)
				// Any fields that are blobs, and encode them.
				if ($this->_table_columns[$c]['data_type'] == 'blob') {
					$this->_object[$c] = $this->blob($this->_object[$c],TRUE);

					// We need to reset our auto_convert flag
					if (isset($this->_table_columns[$c]['auto_convert']))
						$this->_table_columns[$c]['auto_convert'] = FALSE;

				// Any fields that should be seriailzed, we'll do that.
				} elseif (is_array($this->_object[$c]) AND in_array($c,$this->_serialize_column)) {
					$this->_object[$c] = serialize($this->_object[$c]);
				}

		return parent::save($validation);
	}

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

	/**
	 * Retrieve and Store DB BLOB data.
	 */
	private function blob($data,$set=FALSE) {
		return $set ? gzcompress(serialize($data)) : unserialize(gzuncompress($data));
	}

	public function config($key) {
		$mc = Config::instance()->so->module_config($this->_object_name);

		return empty($mc[$key]) ? '' : $mc[$key];
	}

	protected function _where_active() {
		return $this->where('status','=',TRUE);
	}

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

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

	public function list_count($active=TRUE) {
		$a=($active ? $this->_where_active() : $this);

		return $a->find_all()->count();
	}
}
?>