<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

namespace yii\sphinx;
use yii\db\Expression;

/**
 * Class QueryBuilder
 *
 * @author Paul Klimov <klimov.paul@gmail.com>
 * @since 2.0
 */
class QueryBuilder extends \yii\db\mysql\QueryBuilder
{
	/**
	 * Generates a SELECT SQL statement from a [[Query]] object.
	 * @param Query $query the [[Query]] object from which the SQL statement will be generated
	 * @return array the generated SQL statement (the first array element) and the corresponding
	 * parameters to be bound to the SQL statement (the second array element).
	 */
	public function build($query)
	{
		$params = $query->params;
		$clauses = [
			$this->buildSelect($query->select, $query->distinct, $query->selectOption),
			$this->buildFrom($query->from),
			$this->buildWhere($query->where, $params),
			$this->buildGroupBy($query->groupBy),
			$this->buildWithin($query->within),
			$this->buildOrderBy($query->orderBy),
			$this->buildLimit($query->limit, $query->offset),
			$this->buildOption($query->options),
		];
		return [implode($this->separator, array_filter($clauses)), $params];
	}

	/**
	 * @param array $columns
	 * @return string the ORDER BY clause built from [[query]].
	 */
	public function buildWithin($columns)
	{
		if (empty($columns)) {
			return '';
		}
		$orders = [];
		foreach ($columns as $name => $direction) {
			if (is_object($direction)) {
				$orders[] = (string)$direction;
			} else {
				$orders[] = $this->db->quoteColumnName($name) . ($direction === Query::SORT_DESC ? ' DESC' : '');
			}
		}
		return 'WITHIN GROUP ORDER BY ' . implode(', ', $orders);
	}

	/**
	 * @param array $options
	 * @return string the OPTION clause build from [[query]]
	 */
	public function buildOption(array $options)
	{
		if (empty($options)) {
			return '';
		}
		$optionLines = [];
		foreach ($options as $name => $value) {
			$optionLines[] = $name . ' = ' . $value;
		}
		return 'OPTION ' . implode(', ', $optionLines);
	}

	/**
	 * Creates an INSERT SQL statement.
	 * For example,
	 *
	 * ~~~
	 * $sql = $queryBuilder->insert('tbl_user', [
	 *	 'name' => 'Sam',
	 *	 'age' => 30,
	 * ], $params);
	 * ~~~
	 *
	 * The method will properly escape the table and column names.
	 *
	 * @param string $table the table that new rows will be inserted into.
	 * @param array $columns the column data (name => value) to be inserted into the table.
	 * @param array $params the binding parameters that will be generated by this method.
	 * They should be bound to the DB command later.
	 * @return string the INSERT SQL
	 */
	public function insert($table, $columns, &$params)
	{
		if (($tableSchema = $this->db->getTableSchema($table)) !== null) {
			$columnSchemas = $tableSchema->columns;
		} else {
			$columnSchemas = [];
		}
		$names = [];
		$placeholders = [];
		foreach ($columns as $name => $value) {
			$names[] = $this->db->quoteColumnName($name);
			if ($value instanceof Expression) {
				$placeholders[] = $value->expression;
				foreach ($value->params as $n => $v) {
					$params[$n] = $v;
				}
			} else {
				$phName = self::PARAM_PREFIX . count($params);
				$placeholders[] = $phName;
				$params[$phName] = !is_array($value) && isset($columnSchemas[$name]) ? $columnSchemas[$name]->typecast($value) : $value;
			}
		}

		return 'INSERT INTO ' . $this->db->quoteTableName($table)
		. ' (' . implode(', ', $names) . ') VALUES ('
		. implode(', ', $placeholders) . ')';
	}
}