<?php

  
/**
   * Each Expression defines how a QuerySet is built and how a database
   * query is execution. Expressions can be used in conjunction with
   * ExpressionNode objects to create complex queries.
   */
  
class Expression {

    
/**
     * Defines whether this expression should be reversed with a NOT expression
     */
    
protected $not false;

    
/**
     * Defines what SQL comparison operator should be used
     * (=, LIKE, IN, etc)
     *
     * @var string
     */
    
private $operator null;

    
/**
     * What table field the operator is testing against
     *
     * @var string
     */
    
private $field null;

    
/**
     * What value the field and operator are testing
     *
     * @var mixed
     */
    
private $value null;

    
/**
     * Create a new Expression object
     * 
     * @see Field::__callStatic()
     *
     * @param string $field
     * @param string $operator
     * @param mixed $value
     */
    
public function __construct($field$operator$value) {

      
$this->field $field;
      
// defualt operator: "exact", for field = 'value' queries
      
if (!$operator)
        
$operator "exact";
      
$this->operator $operator;

      
// requires a value that is not null
      
if (!$value)
        throw new 
Exception("Cannot create Expression without test value.");
      
$this->value $value;
      
    }
    
    
/**
     * Returns this Expressions' operator
     *
     * @return string
     */
    
public function getOperator() {
      return 
$this->operator;
    }

    
/**
     * Returns this Expressions' field
     * 
     * @return string
     */
    
public function getField() {
      return 
$this->field;
    }

    
/**
     * Return's this Expressions' value
     *
     * @return mixed
     */
    
public function getValue() {
      return 
$this->value;
    }

    
/**
     * Returns whether or not this Expression should lead with
     * 'NOT';
     *
     * @return boolean
     */
    
public function isNot() {
      return 
$this->not;
    }

    
/**
     * Toggles the $not property of this Q depending on it's
     * existing value
     *
     * @see Q::$not
     */
    
public function set_not() {
      if (!
$this->not$this->not true;
      else 
$this->not false;
      return 
$this;
    }

  }

  
/**
   * Set this Q or Expression as a 'NOT' query
   * Cannot be applied to an F field reference object
   */
  
function _NOT($expression) {    
    if (
$expression instanceof F)
      throw new 
Exception("Cannot modify a field reference expression as NOT.");

    
$expression->set_not();
    return 
$expression;
  }

  class 
Field {

    
/**
     * Create a new Expression for QuerySet objects
     *
     * @param string $func
     * @param string|Expression $value
     * @return Expression
     */
    
public static function __callStatic($func$value) {
      list(
$field$operator) = split("__"$func);
      
$value $value[0];

      
/**
       * if the provided value is an expression itself, this is a complex
       * qurey that requires use of an F expression node (for self-referencing
       * fields), e.g:
       *
       * Field::price__gt(Field::cost__times(2))
       *
       * Should produce a query like:
       *
       * price > cost * 2
       *
       * where 'Field::cost__times(2)' is the subexpression
       */
      
if (in_array(strtoupper($operator), array("TIMES""DIVIDED_BY""PLUS""MINUS"))) {

        switch(
strtoupper($operator)) {
          case 
"TIMES":
            
$operator "*";
            break;
          case 
"DIVIDED_BY":
            
$operator "/";
            break;
          case 
"PLUS":
            
$operator "+";
            break;
          case 
"MINUS":
            
$operator "-";
            break;
        }

        if (
$value instanceof F) {
          
$f $value->add($field$operator);
        } else {
          
$f = new F($field);
          
$f $f->add($value$operator);
        }

        return 
$f;
      } else {
        return new 
Expression($field$operator$value);
      }
    }

  }