<?php
/**
 * core.framework
 *
 * @category  Core
 * @package   Core_QueryLanguage
 * @copyright Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license   proprietary
 */

/**
 * Query for QueryLanguage - agnostic representation of a query
 *
 * @category  Core
 * @package   Core_QueryLanguage
 * @copyright Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license   proprietary
 */
class Core_QueryLanguage_Query
{
    const OPERATOR_EQUAL                = '=';
    const OPERATOR_NOT_EQUAL            = '!=';
    const OPERATOR_CONTAINS             = ':';
    const OPERATOR_DOES_NOT_CONTAIN     = '!:';
    const OPERATOR_GREATER              = '>';
    const OPERATOR_GREATER_OR_EQUAL     = '>=';
    const OPERATOR_LOWER                = '<';
    const OPERATOR_LOWER_OR_EQUAL       = '<=';

    const SORT_ASCENDING                = 'ASC';
    const SORT_DESCENDING               = 'DESC';

    const LOGICAL_AND                   = '&';
    const LOGICAL_NOT                   = '!';
    const LOGICAL_OR                    = '|';

    /**
     *
     * @var array
     */
    protected $_select;

    /**
     *
     * @var array
     */
    protected $_query;

    /**
     *
     * @var array
     */
    protected $_order;

    /**
     *
     * @var int
     */
    protected $_offset;

    /**
     *
     * @var int
     */
    protected $_limit;

    /**
     *
     * @var array
     */
    protected static $_constants;

    /**
     *
     * @param string $operator
     *
     * @return boolean
     */
    public function isOperator($operator)
    {
        if (null === self::$_constants) {
            $reflect = new ReflectionClass($this);
            self::$_constants = $reflect->getConstants();
        }

        $test = array_search($operator, self::$_constants, true);
        if (is_string($test) && strpos($test, 'OPERATOR') === 0) {
            return true;
        }

        return false;
    }

    /**
     *
     * @param string|array $select
     *
     * @return \Core_QueryLanguage_Query
     */
    public function setSelect($select)
    {
        if (is_string($select)) {
            $select = explode(',', $select);
        }

        $this->_select = $select;
//        $arrayObject = new ArrayObject;
//
//        array_walk_recursive($select, function(&$item, $key, ArrayObject $arrayObject) {
//            if (strpos($item, '.') !== false) {
//                $oldItem = $item;
//                $array = array();
//                foreach (explode('.', $oldItem) as $part) {
//                    $part = trim($part);
//                    $arrayObject = &$arrayObject[$part];
//                }
//                $item = $arrayObject;
//            } else {
//                $item = trim($item);
//            }
//        }, $arrayObject);
//
//        $this->_select = $arrayObject->getArrayCopy();
//
////        print_r($select);
////        print_r($this->_select);

        return $this;
    }

    /**
     *
     * @return array
     */
    public function getSelect()
    {
        return $this->_select;
    }

    /**
     *
     * @param string|array $query
     *
     * @return \Core_QueryLanguage_Query
     */
    public function setQuery($query)
    {
        $this->_query = $query;

        return $this;
    }

    /**
     *
     * @return array
     */
    public function getQuery()
    {
        return $this->_query;
    }

    /**
     *
     * @param string|array $order
     *
     * @return \Core_QueryLanguage_Query
     */
    public function setOrder($order)
    {
        if (is_string($order)) {
            $stringOrder = $order;
            $order = array();
            foreach (explode(',', $stringOrder) as $key => $value) {
                list($field, $sort) = explode(' ', trim($value));
                $order[trim($field)] = strtoupper(trim($sort));
            }
        }

        $this->_order = $order;

        return $this;
    }

    /**
     *
     * @return array
     */
    public function getOrder()
    {
        return $this->_order;
    }

    /**
     *
     * @param int $offset
     *
     * @return \Core_QueryLanguage_Query
     * @throws LogicException
     */
    public function setOffset($offset)
    {
        if ($offset < 0) {
            throw new LogicException('Error when setting offset, must be greather or equal to 0, tried to set '. $offset);
        }

        $this->_offset = $offset;

        return $this;
    }

    /**
     *
     * @return int
     */
    public function getOffset()
    {
        return $this->_offset;
    }

    /**
     *
     * @param int $limit
     *
     * @return \Core_QueryLanguage_Query
     * @throws LogicException
     */
    public function setLimit($limit)
    {
        if ($limit <= 0) {
            throw new LogicException('Error when setting limit, must be greather than 0, tried to set '. $limit);
        }

        $this->_limit = $limit;

        return $this;
    }

    /**
     *
     * @return int
     */
    public function getLimit()
    {
        return $this->_limit;
    }

    /**
     *
     * @return array
     */
    public function toArray()
    {
        $select = $this->getSelect();
        $query  = $this->getQuery();
        $order  = $this->getOrder();
        $offset = $this->getOffset();
        $limit  = $this->getLimit();

        return compact('select', 'query', 'order', 'offset', 'limit');
    }

    /**
     *
     * @return string
     */
    public function __toString()
    {
        $data = $this->toArray();
        return json_encode($data);
    }

    /**
     *
     * @param string|array $data
     *
     * @return \Core_QueryLanguage_Query
     * @throws InvalidArgumentException
     */
    static public function factory($data)
    {
        if (!is_string($data) && !is_array($data)) {
            throw new InvalidArgumentException('Error while creating Query object,'.
                ' argument needs to be array or string, '. gettype($data) .' given');
        }

        $query = new Core_QueryLanguage_Query;

        if (is_string($data)) {
            $data = Core_QueryLanguage_Parser::parse($data);
        }

        if (is_array($data)) {
            // Get everything else besides select, query, order, limit and offset...
            $queryPart = array_diff_key($data, array_flip(array('select', 'query', 'order', 'limit', 'offset')));

            // ... add to query
            foreach ($queryPart as $field => $value) {
                $data['query'][] = $field . self::OPERATOR_EQUAL . $value;
            }

            if (isset($data['select'])) {
                $query->setSelect($data['select']);
            }

            if (isset($data['query'])) {
                $query->setQuery($data['query']);
            }

            if (isset($data['order'])) {
                $query->setOrder($data['order']);
            }

            if (isset($data['offset'])) {
                $query->setOffset($data['offset']);
            }

            if (isset($data['limit'])) {
                $query->setLimit($data['limit']);
            }
        }

        return $query;
    }
}
