<?php

class SqlCriteria
{
    private $select = array();

    private $distinct = '';

    private $where = array();

    private $group_by = array();

    private $order = array('sort' => array(), 'order' => array());

    private $limit = '';

    /**
     * @var SqlModel
     */
    private $model;

    private $sql_expression;

    private $sql_expression_params = array();

    /**
     * @param $model SqlModel
     * @param $sql_expression string|null Sql expression with SELECT and FROM operators. If fetched, then SqlCriteria::select(), SqlCriteria::distinct() disabled for use.
     * @param $sql_expression_params array additional params to be replaced in sql expression
     */
    public function __construct($model, $sql_expression = null, $sql_expression_params = array())
    {
        $this->model = $model;
        $this->sql_expression = $sql_expression;
        $this->sql_expression_params = $sql_expression_params;
    }

    /**
     * @return SqlResultProvider
     */
    public function find()
    {
        return $this->model->find($this->select, $this->distinct, $this->where, $this->order, $this->limit, null, $this->group_by, $this->sql_expression, $this->sql_expression_params);
    }

    public function count()
    {
        return $this->model->find(new DbExpr('COUNT(*) as count'), '', $this->where, null, null, null, null, $this->sql_expression, $this->sql_expression_params)->fetchField('count');
    }

    /**
     * @param $cond string|array Condition with "?" placeholder @ex 'field=?' or 'field=1' or array('field=?' => 1', 'field=1')
     * @param $value string|array|DbExpr|null Value. Array transformed to DbExpr(implode(',' Array)) All elements in the array mast be integer
     * @return SqlCriteria
     * @desc Allow multiple calls
     */
    public function where($cond, $value = null)
    {
        if (is_null($value)) {
            if (is_array($cond)) {
                $this->where = $this->where + $cond;
            } else {
                $this->where[] = $cond;
            }
        } else {
            $this->where[$cond] = $value;
        }
        return $this;
    }

    /**
     * @param $field string
     * @param $value array
     * @return SqlCriteria
     */
    public function whereIn($field, $value)
    {
        return $this->where($field . ' in ?', $value);
    }

    /**
     * @param $field string
     * @param $value array
     * @return SqlCriteria
     */
    public function whereNotIn($field, $value)
    {
        return $this->where($field . ' not in ?', $value);
    }

    /**
     * @param $field string
     * @param $value array
     * @return SqlCriteria
     * @deprecated
     */
    public function whereNot($field, $value)
    {
        return $this->whereNotIn($field, $value);
    }

    public function groupBy($fields)
    {
        if (is_array($fields)) {
            $this->group_by = $this->group_by + $fields;
        } else {
            $this->group_by[] = $fields;
        }
        return $this;
    }

    /**
     * @param $field string Field @ex 'field'
     * @param $order_desc bool Descendant sort direction
     * @return SqlCriteria
     * @desc Allow multiple calls
     */
    public function order($field, $order_desc = false)
    {
        $this->order['sort'][] = $field;
        if ($order_desc) {
            $this->order['order'][$field] = 'desc';
        }
        return $this;
    }

    /**
     * @param $offset int
     * @param $limit int
     * @return SqlCriteria
     */
    public function limit($offset = 0, $limit)
    {
        if ($offset) {
            $this->limit = (int) $offset . ',';
        }
        $this->limit .= (int) $limit;
        return $this;
    }

    /**
     * @param string|array $fields
     * @ex SqlCriteria::select('field')
     * @ex SqlCriteria->select(array('field1', 'field2'))
     * @ex SqlCriteria->select('field1,field2')
     * @return SqlCriteria
     */
    public function select($fields)
    {
        if (!is_array($fields)) {
            $fields = explode(',', $fields);
        }
        $fields = array_map(function($item){return trim($item);},$fields);
        $this->select = array_merge($this->select,$fields);
        return $this;
    }

    /**
     * @param $field string|bool If true then distinct by *
     * @return SqlCriteria
     */
    public function distinct($field)
    {
        $this->distinct = $field;
        return $this;
    }
}