<?php
namespace nur\sery\php\time;

/**
 * Class Elapsed: durée entre deux événements
 */
class Elapsed {
  const MINUTE = 60;
  const HOUR = 60 * 60;
  const DAY = 60 * 60 * 24;

  /** @var int résolution */
  const RESOLUTION_SECONDS = 0, RESOLUTION_MINUTES = 1, RESOLUTION_HOURS = 2, RESOLUTION_DAYS = 3;

  const DEFAULT_RESOLUTION = self::RESOLUTION_SECONDS;

  private static function format(int $seconds, int $resolution, ?string $prefix=null, ?string $zero=null): string {
    if ($prefix === null) $prefix = "depuis";
    switch ($resolution) {
    case self::RESOLUTION_DAYS: return self::format_days($seconds, $prefix, $zero);
    case self::RESOLUTION_HOURS: return self::format_hours($seconds, $prefix, $zero);
    case self::RESOLUTION_MINUTES: return self::format_minutes($seconds, $prefix, $zero);
    default: return self::format_seconds($seconds, $prefix, $zero);
    }
  }

  private static function format_generic(string $prefix, int $d, int $h, int $m): string {
    $text = $prefix;
    if ($d > 0) {
      if ($text !== "") $text .= " ";
      $text .= sprintf("%d jour", $d);
      if ($d > 1) $text .= "s";
    }
    if ($h > 0) {
      if ($text !== "") $text .= " ";
      $text .= sprintf("%d heure", $h);
      if ($h > 1) $text .= "s";
    }
    if ($m > 0) {
      if ($text !== "") $text .= " ";
      $text .= sprintf("%d minute", $m);
      if ($m > 1) $text .= "s";
    }
    return $text;
  }

  private static function format_seconds(int $seconds, string $prefix, ?string $zero): string {
    $seconds = abs($seconds);

    if ($zero === null) $zero = "maintenant";
    if ($seconds == 0) return $zero;

    if ($seconds <= 3) {
      if ($prefix !== "") $prefix .= " ";
      return "${prefix}quelques secondes";
    }

    if ($seconds < 60) {
      if ($prefix !== "") $prefix .= " ";
      return sprintf("${prefix}%d secondes", $seconds);
    }

    $oneDay = 60 * 60 * 24;
    $oneHour = 60 * 60;
    $oneMinute = 60;
    $rs = $seconds;
    $d = intdiv($rs, $oneDay); $rs = $rs % $oneDay;
    $h = intdiv($rs, $oneHour); $rs = $rs % $oneHour;
    $m = intdiv($rs, $oneMinute);
    return self::format_generic($prefix, $d, $h, $m);
  }

  private static function format_minutes(int $seconds, string $prefix, ?string $zero): string {
    $minutes = intdiv(abs($seconds), 60);

    if ($zero === null) $zero = "maintenant";
    if ($minutes == 0) return $zero;

    if ($minutes <= 3) {
      if ($prefix !== "") $prefix .= " ";
      return "${prefix}quelques minutes";
    }

    $oneDay = 60 * 24;
    $oneHour = 60;
    $rs = $minutes;
    $d = intdiv($rs, $oneDay); $rs = $rs % $oneDay;
    $h = intdiv($rs, $oneHour); $rs = $rs % $oneHour;
    $m = $rs;
    return self::format_generic($prefix, $d, $h, $m);
  }

  private static function format_hours(int $seconds, string $prefix, ?string $zero): string {
    $minutes = intdiv(abs($seconds), 60);

    if ($zero === null) $zero = "maintenant";
    if ($minutes == 0) return $zero;

    if ($minutes < 60) {
      if ($prefix !== "") $prefix .= " ";
      return "${prefix}moins d'une heure";
    } elseif ($minutes < 120) {
      if ($prefix !== "") $prefix .= " ";
      return "${prefix}moins de deux heures";
    }

    $oneDay = 60 * 24;
    $oneHour = 60;
    $rs = $minutes;
    $d = intdiv($rs, $oneDay); $rs = $rs % $oneDay;
    $h = intdiv($rs, $oneHour);
    return self::format_generic($prefix, $d, $h, 0);
  }

  private static function format_days(int $seconds, string $prefix, ?string $zero): string {
    $hours = intdiv(abs($seconds), 60 * 60);

    if ($zero === null) $zero = "aujourd'hui";
    if ($hours < 24) return $zero;

    $oneDay = 24;
    $rs = $hours;
    $d = intdiv($rs, $oneDay);
    return self::format_generic($prefix, $d, 0, 0);
  }
  
  static function format_at(DateTime $start, ?DateTime $now=null, ?int $resolution=null): string {
    $now ??= new DateTime();
    $seconds = $now->getTimestamp() - $start->getTimestamp();
    return (new self($seconds, $resolution))->formatAt();
  }
  
  static function format_since(DateTime $start, ?DateTime $now=null, ?int $resolution=null): string {
    $now ??= new DateTime();
    $seconds = $now->getTimestamp() - $start->getTimestamp();
    return (new self($seconds, $resolution))->formatSince();
  }
  
  static function format_delay(DateTime $start, ?DateTime $now=null, ?int $resolution=null): string {
    $now ??= new DateTime();
    $seconds = $now->getTimestamp() - $start->getTimestamp();
    return (new self($seconds, $resolution))->formatDelay();
  }

  function __construct(int $seconds, ?int $resolution=null) {
    $resolution ??= static::DEFAULT_RESOLUTION;
    if ($resolution < self::RESOLUTION_SECONDS) $resolution = self::RESOLUTION_SECONDS;
    elseif ($resolution > self::RESOLUTION_DAYS) $resolution = self::RESOLUTION_DAYS;
    $this->seconds = $seconds;
    $this->resolution = $resolution;
  }

  /** @var int */
  private $seconds;

  /** @var int */
  private $resolution;

  function formatAt(): string {
    $seconds = $this->seconds;
    if ($seconds < 0) return self::format($seconds, $this->resolution, "dans");
    else return self::format($seconds, $this->resolution, "il y a");
  }

  function formatSince(): string {
    $seconds = $this->seconds;
    if ($seconds < 0) return self::format(-$seconds, $this->resolution, "dans");
    else return self::format($seconds, $this->resolution, "depuis");
  }

  function formatDelay(): string {
    return self::format($this->seconds, $this->resolution, "", "immédiat");
  }
}
