<?php

/**
 * Provides a record-view for mysqli statements
 *
 * By default streams unbuffered data, but seek(), rewind(), or numRecords() will force the statement to
 * buffer itself and sacrifice any potential performance benefit.
 *
 * @package framework
 * @subpackage model
 */
class MySQLStatement extends SS_Query {

	/**
	 * The related mysqli statement object if generated using a prepared query
	 *
	 * @var mysqli_stmt
	 */
	protected $statement;

	/**
	 * Metadata result for this statement
	 *
	 * @var mysqli_result
	 */
	protected $metadata;

	/**
	 * Is the statement bound to the current resultset?
	 *
	 * @var bool
	 */
	protected $bound = false;

	/**
	 * List of column names
	 *
	 * @var array
	 */
	protected $columns = array();

	/**
	 * List of bound variables in the current row
	 *
	 * @var array
	 */
	protected $boundValues = array();

	/**
	 * Binds this statement to the variables
	 */
	protected function bind() {
		$variables = array();

		// Bind each field
		while($field = $this->metadata->fetch_field()) {
			$this->columns[] = $field->name;
			// Note that while boundValues isn't initialised at this point,
			// later calls to $this->statement->fetch() Will populate
			// $this->boundValues later with the next result.
			$variables[] = &$this->boundValues[$field->name];
		}

		$this->bound = true;
		$this->metadata->free();

		// Buffer all results
		$this->statement->store_result();

		call_user_func_array(array($this->statement, 'bind_result'), $variables);
	}

	/**
	 * Hook the result-set given into a Query class, suitable for use by SilverStripe.
	 * @param mysqli_stmt $statement The related statement, if present
	 * @param mysqli_result $metadata The metadata for this statement
	 */
	public function __construct($statement, $metadata) {
		$this->statement = $statement;
		$this->metadata = $metadata;

		// Immediately bind and buffer
		$this->bind();
	}

	public function __destruct() {
		$this->statement->close();
		$this->closed = true;
		$this->currentRecord = false;
	}

	public function seek($row) {
		$this->rowNum = $row - 1;
		$this->statement->data_seek($row);
		return $this->next();
	}

	public function numRecords() {
		return $this->statement->num_rows();
	}

	public function nextRecord() {
		// Skip data if out of data
		if (!$this->statement->fetch()) {
			return false;
		}

		// Dereferenced row
		$row = array();
		foreach($this->boundValues as $key => $value) {
			$row[$key] = $value;
		}
		return $row;
	}

	public function rewind() {
		return $this->seek(0);
	}

}