/**
 * @license
 * Copyright 2022-2025 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { InternalError, isDeepEqual } from "@matter/general";
import { Conformance } from "../../aspects/index.js";
import { FeatureBitmap } from "./FeatureBitmap.js";
function IllegalFeatureCombinations(cluster) {
  const illegal = [];
  const choices = {};
  for (const f of cluster.features) {
    addFeatureNode(f, f.conformance.ast, illegal, choices);
  }
  function add(flags) {
    if (!illegal.some((e) => isDeepEqual(e, flags))) {
      illegal.push(flags);
    }
  }
  let requiresFeatures = false;
  for (const choice of Object.values(choices)) {
    if (choice.exclusive) {
      for (const f1 of choice.features) {
        for (const f2 of choice.features) {
          if (f1 !== f2) {
            add({ [f1]: true, [f2]: true });
          }
        }
      }
    }
    const flags = FeatureBitmap();
    for (const f of choice.features) {
      flags[f] = false;
    }
    add(flags);
    requiresFeatures = true;
  }
  return { illegal, requiresFeatures };
}
function addFeatureNode(feature, node, illegal, choices) {
  function unsupported() {
    throw new InternalError(`New rule required to support ${feature.path} conformance "${feature.conformance}"`);
  }
  function extractName(node2) {
    if (node2.type === Conformance.Special.Name) {
      return node2.param;
    }
    unsupported();
    return "";
  }
  function extractFeatureFlag(node2) {
    switch (node2.type) {
      case Conformance.Special.Name:
        return { [node2.param]: true };
      case Conformance.Operator.NOT:
        return { [extractName(node2.param)]: false };
      default:
        unsupported();
    }
  }
  function addExclusivityRequirement(flags, node2) {
    switch (node2.type) {
      case Conformance.OR:
        addExclusivityRequirement(flags, node2.param.lhs);
        addExclusivityRequirement(flags, node2.param.rhs);
        break;
      default:
        Object.assign(flags, extractFeatureFlag(node2));
        break;
    }
  }
  function addDependencyRequirement(feature2, node2) {
    switch (node2.type) {
      case Conformance.Special.Name:
        illegal.push({ [feature2]: true, [node2.param]: false });
        break;
      case Conformance.AND:
        addDependencyRequirement(feature2, node2.param.lhs);
        addDependencyRequirement(feature2, node2.param.rhs);
        break;
      default:
        unsupported();
    }
  }
  function extractDisjunctFeatures(node2) {
    const result = {};
    function extract(node3, invert = false) {
      switch (node3.type) {
        case Conformance.Special.Name:
          result[node3.param] = !invert;
          break;
        case Conformance.Operator.OR:
          extract(node3.param.lhs, invert);
          extract(node3.param.rhs, invert);
          break;
        case Conformance.Operator.NOT:
          extract(node3.param, !invert);
          break;
        default:
          unsupported();
      }
    }
    extract(node2);
    return result;
  }
  switch (node.type) {
    case Conformance.Special.Desc:
    case Conformance.Special.Empty:
    case Conformance.Flag.Optional:
    case Conformance.Flag.Provisional:
      break;
    case Conformance.Flag.Deprecated:
    case Conformance.Flag.Disallowed:
      illegal.push({ [feature.name]: true });
      break;
    case Conformance.Special.Group:
      node.param.forEach((ast) => addFeatureNode(feature, ast, illegal, choices));
      break;
    case Conformance.Special.Choice:
      if (node.param.num > 1) {
        unsupported();
      }
      const choice = choices[node.param.name];
      if (choice) {
        choice.features.push(feature.name);
      } else {
        choices[node.param.name] = choices[node.param.name] = {
          exclusive: !node.param.orMore,
          features: [feature.name]
        };
      }
      break;
    case Conformance.Special.Name:
      illegal.push({ [node.param]: true, [feature.name]: false });
      break;
    case Conformance.Special.OptionalIf:
      switch (node.param.type) {
        case Conformance.AND:
        case Conformance.Special.Name:
          addDependencyRequirement(feature.name, node.param);
          break;
        case Conformance.OR: {
          const flags = FeatureBitmap({ [feature.name]: true });
          addExclusivityRequirement(flags, node.param);
          illegal.push(flags);
          break;
        }
        case Conformance.Operator.NOT: {
          illegal.push({ [feature.name]: true, [extractName(node.param.param)]: true });
          break;
        }
        default:
          unsupported();
      }
      break;
    case Conformance.Operator.AND: {
      const lhsFeatures = extractDisjunctFeatures(node.param.lhs);
      const rhsFeature = extractFeatureFlag(node.param.rhs);
      for (const lhsFeature in lhsFeatures) {
        illegal.push({
          feature: false,
          [lhsFeature]: lhsFeatures[lhsFeature],
          ...rhsFeature
        });
      }
      break;
    }
    default:
      unsupported();
      break;
  }
}
export {
  IllegalFeatureCombinations
};
//# sourceMappingURL=IllegalFeatureCombinations.js.map
