<?php
namespace nur\m\sqlite;

use nur\func;
use nur\m\base\AbstractRowIterator;
use nur\m\IRowIncarnation;
use SQLite3Stmt;

class SqliteRowIterator extends AbstractRowIterator {
  /** @var SqliteConn */
  protected $conn;

  /** @var string|null */
  protected $sql;

  /** @var IRowIncarnation|null */
  protected $incarnation;

  /**
   * @var SQLite3Stmt instance de SqliteStatement permettant de récupérer les
   * lignes résultat
   */
  protected $stmt;

  /**
   * @var callable|null fonction permettant de (re)créer $stmt. par défaut,
   * $stmt est créé à partir de $sql et $bindings
   */
  protected $createStmtFunc;

  function __construct(SqliteConn $conn, ?string $sql=null, ?array &$bindings=null, ?IRowIncarnation $incarnation=null) {
    parent::__construct();
    $this->conn = $conn;
    $this->reset($sql, $bindings, $incarnation);
  }

  function reset(?string $sql, ?array &$bindings=null, ?IRowIncarnation $incarnation=null): self {
    if ($bindings === null) $bindings = [];
    $this->sql = $sql;
    $this->data =& $bindings;
    $this->incarnation = $incarnation;
    return $this;
  }

  function initStmt(?SQLite3Stmt $stmt=null, ?callable $createStmtFunc=null): self {
    $this->stmt = $stmt;
    $this->createStmtFunc = $createStmtFunc;
    return $this;
  }

  /** créer une instance de SqliteStatement */
  protected function createStmt(): SQLite3Stmt {
    $createStmtFunc = $this->createStmtFunc;
    if ($createStmtFunc !== null) {
      return func::call($createStmtFunc, $this->conn);
    } else {
      ["stmt" => $stmt
      ] = $this->conn->_execute($this->sql, $this->data, ["stmt" => true]);
      return $stmt;
    }
  }

  protected function _setup(): void {
    if ($this->stmt === null) {
      $incarnation = $this->incarnation;
      if ($incarnation !== null) {
        $incarnation->prepareBindings($this->data);
      }
      $this->stmt = $this->createStmt();
    }
  }

  protected function _next() {
    return $this->stmt->fetch(Sqlite::FETCH_ASSOC);
  }

  protected function _cook(&$row, &$key): void {
    if ($this->incarnation !== null) {
      $this->incarnation->loadRow($row, $key);
    }
  }

  protected function _teardown(): void {
    if ($this->stmt !== null) {
      $this->stmt->closeCursor();
      $this->stmt = null;
    }
  }

  function key() { return $this->_key(); }
  function current() { return $this->_current(); }
}
