<?php
/**
 * @copyright NetMonsters <team@netmonsters.ru>
 * @link http://netmonsters.ru
 * @package Majestic
 * @subpackage db
 * @since 2011-11-11
 */

abstract class SqlDbDriver extends DbDriver
{

    protected $identifier_quote = '`';



    public function beginTransaction()
    {
        $this->connect();
        $this->driverBeginTransaction();
        return $this;
    }

    public function commit()
    {
        $this->connect();
        $this->driverCommitTransaction();
        return $this;
    }

    public function rollback()
    {
        $this->connect();
        $this->driverRollbackTransaction();
        return $this;
    }

    /**
     * @param string $table
     * @param mixed $data
     * @param mixed $on_duplicate
     * @return int Affected rows count
     */
    public function insert($table, $data, $on_duplicate = array())
    {
        $columns = array();
        foreach ($data as $col => $val) {
            $columns[] = $this->quoteIdentifier($col);
        }
        $values = array_values($data);

        $sql = 'INSERT INTO ' . $this->quoteIdentifier($table)
             . ' (' . implode(', ', $columns) . ') VALUES (' . $this->quote($values) . ')';
        return $this->query($sql)->affectedRows();
    }

    /**
     * @param string $table
     * @param array $data
     * @param mixed $condition
     * @return int Number of updated rows
     */
    public function update($table, $data, $condition = '')
    {
        $set = array();
        foreach ($data as $col => $val) {
            $set[] = $this->quoteIdentifier($col) . '=' . $this->quote($val);
        }
        $where = $this->whereExpr($condition);
        $sql = 'UPDATE ' . $this->quoteIdentifier($table) . ' SET ' . implode(', ', $set)
             . (($where) ? (' WHERE ' . $where) : '');
        return $this->query($sql)->affectedRows();
    }

    /**
     * @param string $table
     * @param mixed $condition
     * @return int
     */
    public function delete($table, $condition = '')
    {
        $where = $this->whereExpr($condition);
        $sql = 'DELETE FROM ' . $this->quoteIdentifier($table) . (($where) ? (' WHERE ' . $where) : '');
        return $this->query($sql)->affectedRows();
    }

    /**
     * @param mixed $value
     * @return string
     */
    public function quote($value)
    {
        if ($value instanceof DbExpr) {
            return (string) $value;
        }
        if (is_array($value)) {
             foreach ($value as &$val) {
                 $val = $this->quote($val);
             }
             return implode(', ', $value);
        }
        return $this->driverQuote($value);
    }

    /**
     * @param string $ident
     * @return string
     */
    public function quoteIdentifier($ident)
    {
        $ident = explode('.', $ident);
        foreach ($ident as &$segment) {
            if (!preg_match('/(\?|:[A-z][A-z0-9_]*+)/u', $segment)) {
                $segment = $this->identifier_quote . $segment . $this->identifier_quote;
            }
        }
        return implode('.', $ident);
    }

    public function quoteInto($text, $value)
    {
        $pos = mb_strpos($text, '?');
        if ($pos === false) {
            return $text;
        }
        return mb_substr($text, 0, $pos) . $this->quote($value) . mb_substr($text, $pos + 1);
    }

    /**
     * @param mixed $where
     * @return string
     * @throws ErrorException
     */
    public function whereExpr($where)
    {
        if (empty($where)) {
            return $where;
        }

        if (!is_array($where)) {
            $where = array($where);
        }
        foreach ($where as $cond => &$term) {
            if (is_int($cond)) {
                if (is_int($term)) {
                    throw new ErrorException('Condition in where expression as integer. ' . $term);
                }
                if ($term instanceof DbExpr) {
                    $term = (string) $term;
                }
            } else {
                if (is_array($term)) {
                    foreach ($term as &$val) {
                        $val = $this->driverQuote($val);
                    }
                    $term = new DbExpr('(' . implode(',', $term) . ')');
                }
                $term = $this->quoteInto($cond, $term);
            }
        }
        return implode(' AND ', $where);
    }

    /**
     * @param mixed $group_by
     * @return string
     * @throws ErrorException
     */
    public function groupByExpr($group_by)
    {
        if (empty($group_by)) {
            return $group_by;
        }

        if (!is_array($group_by)) {
            $group_by = array($group_by);
        }
        foreach ($group_by as &$term) {
            if ($term instanceof DbExpr) {
                $term = (string) $term;
            } else {
                $term = $this->quoteIdentifier($term);
            }
        }
        return implode(',', $group_by);
    }

    /**
     * @param $select array
     * @param $distinct string|bool
     * @return string
     */
    public function selectExpr($select, $distinct = false)
    {
        if (empty($distinct) && empty($select)) {
            return '*';
        }
        if (!is_array($select)) {
            $select = array($select);
        }
        if ($distinct) {
            $distinct = ((is_bool($distinct)) ? '*' : $this->quoteIdentifier($distinct));
            array_unshift($select, new DbExpr('DISTINCT ' . $distinct));
        }
        foreach ($select as $field => &$term) {
            if (is_int($field)) {
                if ($term instanceof DbExpr) {
                    $term = (string) $term;
                } else {
                    $term = $this->quoteIdentifier($term);
                }
            } else {
                $term = $this->quoteIdentifier($field) . ' as ' . $this->quoteIdentifier($term);
            }
        }
        return implode(',', $select);
    }

    public function limitExpr($limit)
    {
        if (empty($limit)) {
            return $limit;
        }
        return implode(',',array_map(function($item){return intval($item);},explode(',',$limit)));
    }

    abstract protected function driverQuote($value);

    abstract protected function driverBeginTransaction();

    abstract protected function driverCommitTransaction();

    abstract protected function driverRollbackTransaction();

}