<?php
namespace nulib\ldap;


class filters {
  private static function _escape(array $parts): string {
    $op = false;
    $first = true;
    $fparts = [];
    $index = 0;
    foreach ($parts as $name => $part) {
      if ($first) {
        $first = false;
        switch ($part) {
        case "&": case "and": $op = "&"; break;
        case "|": case "or": $op = "|"; break;
        case "!": case "not": $op = "!"; break;
        }
        if ($op) {
          if ($index === $name) $index++;
          continue;
        }
      }
      if ($index === $name) {
        # séquentiel
        $index++;
        if (is_array($part)) {
          $fparts[] = self::_escape($part);
        } else {
          str::add_prefix($part, "(");
          str::add_suffix($part, ")");
          $fparts[] = $part;
        }
      } else {
        # associatif
        $name = ldap_escape($name, "", LDAP_ESCAPE_FILTER);
        foreach (A::with($part) as $value) {
          $value = ldap_escape($value, "", LDAP_ESCAPE_FILTER);
          $fparts[] = "($name=$value)";
        }
      }
    }
    $filter = implode("", $fparts);
    if (count($fparts) > 1 || $op === "!") {
      if (!$op) $op = "&";
      $filter = "($op$filter)";
    }
    return $filter;
  }

  static function parse($filter): string {
    if (!$filter) $filter = "objectClass=*";
    return self::_escape(A::with($filter));
  }

  static function not(string $filter): string {
    str::add_prefix($filter, "(");
    str::add_suffix($filter, ")");
    return "(!$filter)";
  }

  /** mettre en échappement ($attr$op$value) en ignorant les wildcards */
  private static function _filter(string $name, string $op, string $value): string {
    $name = ldap_escape($name, "*", LDAP_ESCAPE_FILTER);
    $value = ldap_escape($value, "*", LDAP_ESCAPE_FILTER);
    return "($name$op$value)";
  }

  static function exists(string $name): string {
    return self::_filter($name, "=", "*");
  }

  static function eq(string $name, string $value): string {
    return self::_filter($name, "=", $value);
  }

  static function ge(string $name, string $value): string {
    return self::_filter($name, ">=", $value);
  }

  static function le(string $name, string $value): string {
    return self::_filter($name, "<=", $value);
  }

  static function gt(string $name, string $value): string {
    return self::not(self::le($name, $value));
  }

  static function lt(string $name, string $value): string {
    return self::not(self::ge($name, $value));
  }

  static function approx(string $name, string $value): string {
    return self::_filter($name, "~=", $value);
  }
}
