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

/**
 * This class takes care of building an LDAP filter query
 *
 * @package    PLA
 * @subpackage LDAP/Search
 * @category   Helpers
 * @author     Deon George
 * @copyright  (c) phpLDAPadmin Development Team
 * @license    http://dev.phpldapadmin.org/license.html
 */
abstract class PLA_Database_LDAP_Search_Builder_Query extends Database_Query_Builder {
	protected $_where = array();

	// @todo Not implemented
	public function reset() {}

	public function __construct() {
		parent::__construct(Database::SELECT,'ldap');
	}

	/**
	 * Alias of and_where()
	 *
	 * @param   mixed   column name or array($column, $alias) or object
	 * @param   string  logic operator
	 * @param   mixed   column value
	 * @return  $this
	 */
	public function where($column,$op,$value) {
		return $this->and_where($column,$op,$value);
	}

	/**
	 * Creates a new "AND WHERE" condition for the query.
	 *
	 * @param   mixed   column name or array($column,$alias) or object
	 * @param   string  logic operator
	 * @param   mixed   column value
	 * @return  $this
	 */
	public function and_where($column,$op,$value) {
		$this->_where[] = array('AND' => array($column,$op,$value));

		return $this;
	}

	/**
	 * Creates a new "OR WHERE" condition for the query.
	 *
	 * @param   mixed   column name or array($column,$alias) or object
	 * @param   string  logic operator
	 * @param   mixed   column value
	 * @return  $this
	 */
	public function or_where($column,$op,$value) {
		$this->_where[] = array('OR' => array($column,$op,$value));

		return $this;
	}

	/**
	 * Alias of and_where_open()
	 *
	 * @return  $this
	 */
	public function where_open() {
		return $this->and_where_open();
	}

	/**
	 * Opens a new "AND WHERE (...)" grouping.
	 *
	 * @return  $this
	 */
	public function and_where_open() {
		$this->_where[] = array('AND' => '(');

		return $this;
	}

	/**
	 * Opens a new "OR WHERE (...)" grouping.
	 *
	 * @return  $this
	 */
	public function or_where_open() {
		$this->_where[] = array('OR' => '(');

		return $this;
	}

	public function compile($db = NULL) {
		$filter = '';

		return $this->_compile_conditions($db,$this->_where);
	}

	/**
	 * Closes an open "AND WHERE (...)" grouping.
	 *
	 * @return  $this
	 */
	public function where_close() {
		return $this->and_where_close();
	}

	/**
	 * Closes an open "AND WHERE (...)" grouping.
	 *
	 * @return  $this
	 */
	public function and_where_close() {
		$this->_where[] = array('AND' => ')');

		return $this;
	}

	/**
	 * Closes an open "OR WHERE (...)" grouping.
	 *
	 * @return  $this
	 */
	public function or_where_close() {
		$this->_where[] = array('OR' => ')');

		return $this;
	}

	/**
	 * Compiles an array of conditions into an LDAP filter.
	 *
	 * @param   object  Database instance
	 * @param   array   condition statements
	 * @return  string
	 */
	protected function _compile_conditions(Database $db,array $conditions,$index=0) {
		$current_condition = $last_condition = NULL;

		$filter = '';
		$sub = 0;
		foreach ($conditions as $key => $group) {
			// If we have been called again, we need to skip ahead, or skip what has been processed
			if ($key < $index OR $sub)
				continue;

			// Process groups of conditions
			foreach ($group as $logic => $condition) {
				if ($condition === '(') {
					$filter .= $this->_compile_conditions($db,$conditions,$key+1);
					$sub = 1;

				} elseif ($condition === ')') {
					if ($index) {
						// As we return, we'll include our condition
						switch ($current_condition) {
							case 'AND':
								return '(&'.$filter.')';

							case 'OR':
								return '(|'.$filter.')';

							default:
								throw new Kohana_Exception('Condition :condition not handled.',array(':condition'=>$condition));
						}
					}

					$sub = 0;

				} else {
					// We currently cant handle when a condition changes, without brackets.
					if ($filter AND $current_condition AND $current_condition != $logic)
						throw new Kohana_Exception('Condition changed without brackets');

					$current_condition = $logic;

					// Split the condition
					list($column,$op,$value) = $condition;

					// Database operators are always uppercase
					$op = strtoupper($op);

					if ((is_string($value) AND array_key_exists($value,$this->_parameters)) === FALSE) {
						// Quote the value, it is not a parameter
						$value = $db->quote($value);
					}

					if ($column) {
						// Apply proper quoting to the column
						$column = $db->quote_column($column);
					}

					// Append the statement to the query
					$filter .= trim('('.$column.$op.$value.')');
				}

				$last_condition = $condition;
			}
		}

		switch ($current_condition) {
			case 'AND':
				return '(&'.$filter.')';

			case 'OR':
				return '(|'.$filter.')';

			default:
				throw new Kohana_Exception('Condition :condition not handled.',array(':condition'=>$condition));
		}
	}
}
?>
