<?php
/**
* Base
*
* Desciption
* @package kernel4
*/
class kBase {
	/**
	* Holds reference to global KernelApplication instance
	* @access public
	* @var kApplication
	*/
	var $Application;

	/**
	 * Prefix, that was used
	 * to create an object
	 *
	 * @var string
	 * @access public
	 */
	var $Prefix='';

	/**
	 * Special, that was used
	 * to create an object
	 *
	 * @var string
	 * @access public
	 */
	var $Special='';

	var $OriginalParams;

	/**
	 * Set's application
	 *
	 * @return kBase
	 * @access public
	 */
	function kBase()
	{
		$this->Application =& kApplication::Instance();
	}

	/**
	 * Create new instance of object
	 *
	 * @return kBase
	 */
	function &makeClass()
	{
		return new kBase();
	}

	/**
	 * Set's prefix and special
	 *
	 * @param string $prefix
	 * @param string $special
	 * @access public
	 */
	function Init($prefix,$special,$event_params=null)
	{
		$prefix=explode('_',$prefix,2);
		$this->Prefix=$prefix[0];
		$this->Special=$special;
		$this->OriginalParams = $event_params;
	}

	/**
	 * Returns joined prefix
	 * and special if any
	 *
	 * @param bool $from_submit if true, then joins prefix & special by "_", uses  "." otherwise
	 * @return string
	 * @access protected
	 */
	function getPrefixSpecial($from_submit = false)
	{
		$separator = !$from_submit ? '.' : '_';
		$ret = $this->Prefix.$separator.$this->Special;
		return rtrim($ret, $separator);
	}

	function &getProperty($property_name)
	{
		return $this->$property_name;
	}

	function setProperty($property_name, &$property_value)
	{
		$this->$property_name =& $property_value;
	}
}

	class kHelper extends kBase {

		/**
		* Connection to database
		*
		* @var kDBConnection
		* @access public
		*/
		var $Conn;

		function kHelper()
		{
			parent::kBase();
			$this->Conn =& $this->Application->GetADODBConnection();
		}

		function InitHelper()
		{

		}

	}

class kDBBase extends kBase {
	/**
	* Connection to database
	*
	* @var kDBConnection
	* @access public
	*/
	var $Conn;

	/**
	* Description
	*
	* @var string Name of primary key field for the item
	* @access public
	*/
	var $IDField;

	/**
	* Holds SELECT, FROM, JOIN parts of SELECT query
	*
	* @var string
	* @access public
	*/
	var $SelectClause;

	/**
	* Fields allowed to be set (from table + virtual)
	*
	* @var Array
	* @access private
	*/
	var $Fields=Array();

	/**
	 * All virtual field names
	 *
	 * @var Array
	 * @access private
	 */
	var $VirtualFields=Array();

	/**
	 * Fields that need to be queried using custom expression, e.g. IF(...) AS value
	 *
	 * @var Array
	 * @access private
	 */
	var $CalculatedFields = Array();

	/**
	* Description
	*
	* @var string Item' database table name, without prefix
	* @access public
	*/
	var $TableName;

	/**
	 * Allows to determine object's table status ('temp' - temp table, '' - live table)
	 *
	 * @var string
	 * @access public
	 */
	var $mode='';

	function kDBBase()
	{
		parent::kBase();
		$this->Conn =& $this->Application->GetADODBConnection();
	}

	/**
	* Set current item' database table name
	*
	* @access public
	* @param string $table_name
	* @return void
	*/
	function setTableName($table_name)
	{
		$this->TableName = $table_name;
	}

	/**
	 * Set object' TableName to Live table from config
	 *
	 * @access public
	 */
	function SwitchToLive()
	{
		$this->TableName = $this->Application->getUnitOption($this->Prefix, 'TableName');
	}

	/**
	 * Set object' TableName to Temp table from config
	 *
	 * @access public
	 */
	function SwitchToTemp()
	{
		$this->TableName = $this->Application->getUnitOption($this->Prefix, 'TableName');
		$this->SetTableName( kTempTablesHandler::GetTempName($this->TableName) );
		$this->mode = 't';
	}

	/**
	 * Checks if object uses temp table
	 *
	 * @return bool
	 */
	function IsTempTable()
	{
		return kTempTablesHandler::IsTempTable($this->TableName);
	}

	/**
	* Sets SELECT part of list' query
	*
	* @access public
	* @param string $sql SELECT and FROM [JOIN] part of the query up to WHERE
	* @return void
	*/
	function SetSelectSQL($sql)
	{
		$this->SelectClause = $sql;
	}

	function GetSelectSQL($base_query=null)
	{
		if( !isset($base_query) ) $base_query = $this->SelectClause;
		return $q = str_replace( Array('%1$s','%s'), $this->TableName, $base_query);
	}

	/**
	 * Insert calculated fields sql into query in place of %2$s,
	 * return processed query.
	 *
	 * @param string $query
	 * @return string
	 */
	function addCalculatedFields($query)
	{
		if($this->CalculatedFields)
		{
			$sql = Array();
			foreach($this->CalculatedFields as $field_name => $field_expression)
			{
				$sql[] = '('.$field_expression.') AS '.$field_name;
			}
			$sql = implode(',',$sql);
			return $this->Application->ReplaceLanguageTags( str_replace('%2$s', ','.$sql, $query) );
		}
		else
		{
			return str_replace('%2$s', '', $query);
		}
	}

	/**
	 * Adds calculated field declaration to object.
	 *
	 * @param string $name
	 * @param string $sql_clause
	 */
	function addCalculatedField($name, $sql_clause)
	{
		$this->CalculatedFields[$name] = $sql_clause;
	}

	/**
	* Sets ID Field name used as primary key for loading items
	*
	* @access public
	* @param string $field_name
	* @return void
	* @see kDBBase::IDField
	*/
	function setIDField($field_name)
	{
		$this->IDField = $field_name;
	}

	function Configure()
	{
		$this->setTableName( $this->Application->getUnitOption($this->Prefix, 'TableName') );
		$this->setIDField( $this->Application->getUnitOption($this->Prefix, 'IDField') );

		$this->defineFields();

		$this->ApplyFieldModifiers(); // should be called only after all fields definitions been set
		$this->prepareConfigOptions(); // this should go last, but before setDefaultValues, order is significant!
		$this->SetDefaultValues();
	}

	/**
	 * Add field definitions from all possible sources (DB Fields, Virtual Fields, Calcualted Fields, e.t.c.)
	 *
	 */
	function defineFields()
	{
		$this->setConfigFields( $this->Application->getUnitOption($this->Prefix, 'Fields') );
		$this->setVirtualFields( $this->Application->getUnitOption($this->Prefix, 'VirtualFields') );
		$this->setCalculatedFields( $this->Application->getUnitOption($this->Prefix, 'CalculatedFields') );
	}

	function setCalculatedFields($fields)
	{
		$this->CalculatedFields = isset($fields[$this->Special]) ? $fields[$this->Special] : (isset($fields['']) ? $fields[''] : false);
	}

	/**
	 * Set's field names from table
	 * from config
	 *
	 * @param Array $fields
	 * @access public
	 */
	function setConfigFields($fields)
	{
		$this->Fields = $fields;
	}

	/**
	 * Override field options with ones defined in submit via "field_modfiers" array (common for all prefixes)
	 *
	 * @access private
	 * @author Alex
	 */
	function ApplyFieldModifiers()
	{
//		$this->Application->APCalled[] = $this->getPrefixSpecial();

		$allowed_modifiers = Array('required');

		$field_modifiers = $this->Application->GetVar('field_modifiers');
		if(!$field_modifiers) return false;

		$field_modifiers = getArrayValue($field_modifiers, $this->getPrefixSpecial());
		if(!$field_modifiers) return false;

		foreach ($field_modifiers as $field => $field_options)
		{
			foreach ($field_options as $option_name => $option_value)
			{
				if ( !in_array(strtolower($option_name), $allowed_modifiers) ) continue;
				$this->Fields[$field][$option_name] = $option_value;
			}
		}
	}

	/**
	 * Set fields (+options) for fields that physically doesn't exist in database
	 *
	 * @param Array $fields
	 * @access public
	 */
	function setVirtualFields($fields)
	{
		if($fields)
		{
			$this->VirtualFields = $fields;
			$this->Fields = array_merge_recursive2($this->VirtualFields, $this->Fields);
		}
	}

	function SetDefaultValues()
	{
		foreach($this->Fields as $field => $options)
		{
			if( getArrayValue($options, 'default') === '#NOW#')
			{
				$this->Fields[$field]['default'] = adodb_mktime();
			}
		}
	}

	function SetFieldOptions($field, $options)
	{
		$this->Fields[$field] = $options;
	}

	function GetFieldOptions($field)
	{
		return isset($this->Fields[$field]) ? $this->Fields[$field] : Array();
	}

	/**
	 * Returns formatted field value
	 *
	 * @param string $field
	 * @return string
	 * @access public
	 */
	function GetField($name, $format=null)
	{
		$options = $this->GetFieldOptions($name);
		$val = $this->GetDBField($name);
		$res = $val;
		if (isset($options['formatter'])) {
			$formatter =& $this->Application->recallObject($options['formatter']);
			$res = $formatter->Format($val, $name, $this, $format );
		}
		return $res;
	}

	function HasField($name)
	{

	}

	function GetFieldValues()
	{

	}

	function UpdateFormattersSubFields()
	{
		foreach ($this->Fields as $field => $options) {
			if (isset($options['formatter'])) {
				$formatter =& $this->Application->recallObject($options['formatter']);
				$formatter->UpdateSubFields($field, $this->GetDBField($field), $options, $this);
			}
		}
	}

	function prepareConfigOptions()
	{
		foreach (array_keys($this->Fields) as $field_name)
		{
			$field_options =& $this->Fields[$field_name];
			if( isset($field_options['options_sql']) )
			{
				// replace with query result
				$select_clause = $field_options['option_title_field'].','.$field_options['option_key_field'];
				$sql = sprintf($field_options['options_sql'], $select_clause);

				$options_hash = getArrayValue($field_options,'options');
				if($options_hash === false) $options_hash = Array();

				$dynamic_options = $this->Conn->GetCol($sql, $field_options['option_key_field']);
				$field_options['options'] = array_merge_recursive2($options_hash, $dynamic_options);

				unset($field_options['options_sql']);
			}
			$this->PrepareOptions($field_name);
		}
	}

	function PrepareOptions($field_name)
	{
		if( $formatter_class = getArrayValue($this->Fields[$field_name], 'formatter') )
		{
			$formatter =& $this->Application->recallObject($formatter_class);
			$formatter->PrepareOptions($field_name, $this->Fields[$field_name], $this);
		}
	}

	/**
	 * Returns unformatted field value
	 *
	 * @param string $field
	 * @return string
	 * @access public
	 */
	function GetDBField($field)
	{

	}

	/**
	 * Returns ID of currently processed record
	 *
	 * @return int
	 * @access public
	 */
	function GetID()
	{
		return $this->GetDBField($this->IDField);
	}

	/**
	 * Returns parent table information
	 *
	 * @param bool $from_temp load parent item from temp table
	 * @param string $special special of main item
	 * @return Array
	 */
	function getLinkedInfo($special = '')
	{
		$parent_prefix = $this->Application->getUnitOption($this->Prefix, 'ParentPrefix');
		if($parent_prefix)
		{
			// if this is linked table, then set id from main table
			$table_info = Array(
				'TableName' => $this->Application->getUnitOption($this->Prefix,'TableName'),
				'IdField' => $this->Application->getUnitOption($this->Prefix,'IDField'),
				'ForeignKey' => $this->Application->getUnitOption($this->Prefix,'ForeignKey'),
				'ParentTableKey' => $this->Application->getUnitOption($this->Prefix,'ParentTableKey'),
				'ParentPrefix' => $parent_prefix
			);
			if (is_array($table_info['ForeignKey'])) {
					$table_info['ForeignKey'] = getArrayValue($table_info, 'ForeignKey', $parent_prefix);
			}
			if (is_array($table_info['ParentTableKey'])) {
				$table_info['ParentTableKey'] = getArrayValue($table_info, 'ParentTableKey', $parent_prefix);
			}

			$main_object =& $this->Application->recallObject($parent_prefix.'.'.$special);
			return array_merge($table_info, Array('ParentId'=> $main_object->GetDBField( $table_info['ParentTableKey'] ) ) );
		}
		return false;
	}

}

?>