<?php
namespace nur\b;

use nur\A;
use Throwable;

/**
 * Class ExceptionShadow: une classe qui capture les informations d'une
 * exception afin de pouvoir les sérialiser
 */
class ExceptionShadow {
  protected static function extract_trace(array $trace): array {
    $frames = [];
    foreach ($trace as $frame) {
      $file = A::get($frame, "file");
      $line = A::get($frame, "line");
      $class = A::get($frame, "class");
      $function = A::get($frame, "function");
      $type = A::get($frame, "type");
      $frames[] = [
        "file" => $file,
        "line" => $line,
        "class" => $class,
        "object" => null,
        "type" => $type,
        "function" => $function,
        "args" => [],
      ];
    }
    return $frames;
  }

  function __construct(Throwable $exception) {
    $this->class = get_class($exception);
    $this->message = $exception->getMessage();
    $this->code = $exception->getCode();
    $this->file = $exception->getFile();
    $this->line = $exception->getLine();
    $this->trace = self::extract_trace($exception->getTrace());
    $previous = $exception->getPrevious();
    if ($previous !== null) $this->previous = new static($previous);
  }

  /** @var string */
  protected $class;

  function getClass(): string {
    return $this->class;
  }

  /** @var string */
  protected $message;

  function getMessage(): string {
    return $this->message;
  }

  /** @var mixed */
  protected $code;

  function getCode() {
    return $this->code;
  }

  /** @var string */
  protected $file;

  function getFile(): string {
    return $this->file;
  }

  /** @var int */
  protected $line;

  function getLine(): int {
    return $this->line;
  }

  /** @var array */
  protected $trace;

  function getTrace(): array {
    return $this->trace;
  }

  function getTraceAsString(): string {
    $lines = [];
    foreach ($this->trace as $index => $frame) {
      $lines[] = "#$index $frame[file]($frame[line]): $frame[class]$frame[type]$frame[function]()";
    }
    $index++;
    $lines[] = "#$index {main}";
    return implode("\n", $lines);
  }

  /** @var ExceptionShadow */
  protected $previous;

  function getPrevious(): ?ExceptionShadow {
    return $this->previous;
  }
}