<?php
namespace nur\b\io;

use nur\b\coll\AbstractIterator;
use nur\str;

/**
 * Class StreamReader: lecture depuis un flux
 */
class StreamReader extends AbstractIterator implements IReader {
  use Treader, Tfilter;

  /** @var resource */
  protected $fd;

  /** @var bool */
  protected $close;

  function __construct($fd, ?bool $close=null) {
    if ($close === null) $close = $fd !== null;
    if ($fd === null) $fd = STDIN;
    $this->fd = $fd;
    $this->close = $close;
  }

  function setEncodingFilter(string $from, string $to="utf-8"): void {
    $this->_setEncodingFilter($from, $to);
  }

  /**
   * @throws IOException
   * @return resource
   */
  protected final function open() {
    IOException::ensure_open($this->fd === null);
    $this->_streamAppendFilters($this->fd);
    return $this->fd;
  }

  /** @throws IOException */
  function getResource() {
    return $this->open();
  }

  /** @throws IOException */
  function seek(int $offset=0, int $whence=SEEK_SET): int {
    return fseek($this->open(), $offset, $whence);
  }

  function readLine(): string {
    $line = EOFException::ensure_not_eof(fgets($this->open()));
    return str::strip_nl($line);
  }

  protected function _next(&$key) {
    return $this->readLine();
  }
  function key() { return $this->_key(); }
  function current() { return $this->_current(); }

  function getContents(bool $close=true): string {
    try {
      return stream_get_contents($this->open());
    } finally {
      $this->close($close);
    }
  }

  function close(bool $close=true): void {
    if ($this->fd !== null && $this->close && $close) {
      fclose($this->fd);
      $this->fd = null;
    }
  }
}