<?php
namespace nur;

use nur\b\io\IOException;
use nur\b\io\SharedFile;

/**
 * Class file: méthodes outils pour gérer les fichiers avec support des
 * exceptions
 */
class file {
  /** générer un nom de fichier temporaire avec le préfixe spécifié */
  static function temp(string $prefix, ?string $tmpdir=null): string {
    if ($tmpdir === null) $tmpdir = sys_get_temp_dir();
    return IOException::ensure_not_false(tempnam($tmpdir, $prefix));
  }

  /** copier le fichier $src sur $dest */
  static function copy(string $src, string $dest, bool $overwrite=true): bool {
    if (!$overwrite && file_exists($dest)) {
      throw new IOException("$dest: fichier existant");
    }
    return IOException::ensure_not_false(copy($src, $dest));
  }

  /** obtenir une instance permettant un accès concurrent au fichier spécifié */
  static function with(string $file): SharedFile {
    return new SharedFile($file);
  }

  /**
   * @param resource $context
   * @return resource
   * @throws IOException
   */
  static function open(string $filename, string $mode, bool $use_include_path=false, $context=null) {
    return IOException::ensure_not_false(fopen($filename, $mode, $use_include_path, $context));
  }

  /**
   * @param resource $stream
   * @throws IOException
   */
  static function tell($stream): int {
    return IOException::ensure_not_false(ftell($stream));
  }

  /**
   * @param resource $stream
   * @return int la position après avoir déplacé le pointeur
   * @throws IOException
   */
  static function seek($stream, int $offset, int $whence=SEEK_SET): int {
    $r = fseek($stream, $offset, $whence);
    if ($r == -1) throw IOException::error();
    return self::tell($stream);
  }

  /**
   * @param resource $stream
   * @throws IOException
   */
  static function read($stream, int $length): string {
    return IOException::ensure_not_false(fread($stream, $length));
  }

  /**
   * @param resource $stream
   * @throws IOException
   */
  static function fgets($stream, ?int $length=null): string {
    return IOException::ensure_not_false(fgets($stream, $length));
  }

  /**
   * à partir de la position $pos, trouver le premier caractère \n et retourner
   * la position juste après
   */
  static function find_nl($stream, int $pos): int {
    self::seek($stream, $pos, SEEK_SET);
    while (true) {
      $data = self::read($stream, 512);
      if ($data === "") return $pos + 1;
      $nlpos = strpos($data, "\n");
      if ($nlpos !== false) {
        $pos += $nlpos + 1;
        break;
      } else {
        $pos += strlen($data);
      }
    }
    return $pos;
  }

  /**
   * @param resource $stream
   * @throws IOException
   */
  static function write($stream, string $data, ?int $length=null): int {
    if ($length === null) $result = fwrite($stream, $data);
    else $result = fwrite($stream, $data, $length);
    return IOException::ensure_not_false($result);
  }

  /**
   * @param resource $stream
   * @throws IOException
   */
  static function close($stream, bool $throw=false): bool {
    $closed = fclose($stream);
    if ($closed || !$throw) return $closed;
    else throw IOException::error();
  }
}
