<?php
namespace nur\data\types;

use nur\A;
use nur\b\ValueException;
use nur\func;
use Traversable;

/**
 * Class GenericType: type de données générique, qui n'est ni formatable ni
 * analysable
 */
class GenericType extends AbstractSimpleType {
  const ARRAY = "array";
  const ARRAY_ARRAY = "array[]";
  const ITERABLE = "iterable";
  const RESOURCE = "resource";

  function __construct($class, ?array $params=null) {
    parent::__construct($params);
    func::fix_class_args($class, $args);
    $this->class = $class;
    $this->args = $args;
    #XXX faut-il vérifier que la classe est valide?
  }

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

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

  function getClass(): string {
    $class = $this->class;
    switch ($class) {
    case self::ARRAY_ARRAY: $class = self::ARRAY; break;
    }
    return $class;
  }

  function isInstance($value, bool $strict=false): bool {
    if ($value === null) return $this->ppAllowNull;
    $class = $this->getClass();
    switch ($class) {
    case self::ARRAY:
    case self::ARRAY_ARRAY:
      return is_array($value);
    case self::ITERABLE: return is_iterable($value);
    case self::RESOURCE: return is_resource($value);
    default: return $value instanceof $class;
    }
  }

  function canFormat(): bool {
    return false;
  }

  protected function _verifix(&$value, array &$result=null): void {
    $orig = $value;
    if ($this->verifixNoParse($value, $result)) return;

    $class = $this->getClass();
    switch ($class) {
    case self::ARRAY:
      A::ensure_array($value);
      break;
    case self::ARRAY_ARRAY:
      A::ensure_array($value);
      foreach ($value as &$item) {
        A::ensure_array($item);
      }; unset($item);
      break;
    case self::ITERABLE:
      if (!($value instanceof Traversable)) A::ensure_array($value);
      break;
    case self::RESOURCE:
      if (!is_resource($value)) {
        $value = self::result_invalid($result, "invalid", $orig, null
          , false, false
          , $this->ppMessages, ValueException::unexpected_type(self::RESOURCE, $value));
        return;
      }
      break;
    default:
      $args = $this->args;
      A::merge($args, A::with($value));
      $value = func::cons($class, ...$args);
    }
    self::result_valid($result, $value, $orig);
  }
}
