<?php
namespace nur\ldap\schemas;

use nur\A;
use nur\func;
use nur\ldap\LdapAttr;
use nur\ldap\LdapConn;
use nur\ldap\syntaxes\AbstractSyntax;
use nur\ldap\syntaxes\CompositeSyntax;
use nur\php\Autogen;

class SchemaManager {
  function __construct(LdapConn $conn, ?array $overrides=null) {
    $lse = new LdapSchemaExtractor($conn->getSchemaInfos());
    $lse->init();
    $this->lse = $lse;
    $this->overrides = $overrides;
  }

  /** @var LdapSchemaExtractor */
  protected $lse;

  /** @var array|null */
  protected $overrides;

  function getAttributes(array $objectClasses): array {
    return $this->lse->getAttributes($objectClasses, $this->overrides);
  }

  /** @var AbstractSyntax[] */
  protected $syntaxes;

  function getSyntax($class): AbstractSyntax {
    if (is_array($class)) return func::cons(...$class);
    $syntax = A::get($this->syntaxes, $class);
    if ($syntax === null) {
      $syntax = $this->syntaxes[$class] = func::cons($class);
    }
    return $syntax;
  }

  function autogenSchema(array $objectClasses): array {
    return $this->getAttributes($objectClasses);
  }

  static function fix_type(AbstractSyntax $syntax, bool $monovalued): array {
    if ($syntax instanceof CompositeSyntax) {
      if ($monovalued) $phpType = $syntax->getPhpType();
      else $phpType = $syntax->getAttrClass();
    } else {
      $phpType = $syntax->getPhpType();
      if (!$monovalued) $phpType .= "[]";
    }
    return Autogen::fix_type($phpType);
  }

  function autogenProperties(array $schema): array {
    $properties = [];
    foreach ($schema as $attribute) {
      $name = $attribute["name"];
      /** @var AbstractSyntax $syntax */
      $syntax = $this->getSyntax($attribute["class"]);
      $monovalued = ($attribute["flags"] & LdapAttr::MONOVALUED) != 0;
      [$phpType, $returnType] = self::fix_type($syntax, $monovalued);
      $properties[] = "$returnType \$$name";
    }
    return $properties;
  }

  function autogenMethods(array $schema): array {
    $methods = [];
    foreach ($schema as $attribute) {
      $name = $attribute["name"];
      /** @var AbstractSyntax $syntax */
      $syntax = $this->getSyntax($attribute["class"]);
      $returnType = $syntax instanceof CompositeSyntax? $syntax->getAttrClass(): LdapAttr::class;
      $methods[] = "\\$returnType $name()";
    }
    return $methods;
  }
}