<?php
namespace nur\ldap\schemas;

use nur\b\ValueException;

class LseParser {
  /** supprimer le {size} à la fin d'un OID */
  protected static function fix_oid(string $oid): string {
    return preg_replace('/\{\d+}$/', "", $oid);
  }

  function __construct(?string $s=null) {
    $this->s = $s;
  }

  protected function expected(string $expected): ValueException {
    return new ValueException("expected $expected, got $this->s");
  }
  protected function unexpected(string $value): ValueException {
    return new ValueException("unexpected $value");
  }

  protected $s;

  #~~~~

  const SPACES_PATTERN = '/^\s+/';

  protected function skipSpaces(): void {
    if (preg_match(self::SPACES_PATTERN, $this->s, $ms)) {
      $this->s = substr($this->s, strlen($ms[0]));
    }
  }

  #~~~~
  protected function isLiteral(string $literal): bool {
    return substr($this->s, 0, strlen($literal)) === $literal;
  }

  protected function skipLiteral(string $literal): void {
    $pos = strlen($literal);
    if (substr($this->s, 0, $pos) === $literal) {
      $this->s = substr($this->s, $pos);
    } else {
      throw $this->expected($literal);
    }
    $this->skipSpaces();
  }

  #~~~~

  const NAME_PATTERN = '/^\S+/';

  protected function isName(): bool {
    if (!preg_match(self::NAME_PATTERN, $this->s, $ms)) return false;
    $name = $ms[0];
    return !in_array($name, ['(', ')', '$']);
  }

  protected function parseName(): string {
    if (!preg_match(self::NAME_PATTERN, $this->s, $ms)) {
      throw $this->expected("<NAME>");
    }
    $name = $ms[0];
    $this->s = substr($this->s, strlen($name));
    $this->skipSpaces();
    return $name;
  }
  
  #~~~~

  const STRING_PATTERN = "/^'([^']*)'/";

  protected function isString(): bool {
    return preg_match(self::STRING_PATTERN, $this->s, $ms);
  }

  protected function parseString(): string {
    if (!preg_match(self::STRING_PATTERN, $this->s, $ms)) {
      throw $this->expected("<STRING>");
    }
    $this->s = substr($this->s, strlen($ms[0]));
    $this->skipSpaces();
    return $ms[1];
  }
  
  #~~~~

  protected function parseNames(): array {
    if ($this->isName()) return [$this->parseName()];
    $names = [];
    if ($this->isLiteral('(')) {
      $this->skipLiteral('(');
      while ($this->isName()) {
        $names[] = $this->parseName();
        if ($this->isLiteral('$')) $this->skipLiteral('$');
      }
      $this->skipLiteral(')');
    } else {
      $names[] = $this->parseName();
    }
    return $names;
  }

  protected function parseStrings(): array {
    if ($this->isString()) return [$this->parseString()];
    $strings = [];
    if ($this->isLiteral('(')) {
      $this->skipLiteral('(');
      while ($this->isString()) {
        $strings[] = $this->parseString();
      }
      $this->skipLiteral(')');
    } else {
      $strings[] = $this->parseString();
    }
    return $strings;
  }
}