var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
var __typeError = (msg) => {
  throw TypeError(msg);
};
var __using = (stack, value, async) => {
  if (value != null) {
    if (typeof value !== "object" && typeof value !== "function") __typeError("Object expected");
    var dispose, inner;
    if (async) dispose = value[__knownSymbol("asyncDispose")];
    if (dispose === void 0) {
      dispose = value[__knownSymbol("dispose")];
      if (async) inner = dispose;
    }
    if (typeof dispose !== "function") __typeError("Object not disposable");
    if (inner) dispose = function() {
      try {
        inner.call(this);
      } catch (e) {
        return Promise.reject(e);
      }
    };
    stack.push([async, dispose, value]);
  } else if (async) {
    stack.push([async]);
  }
  return value;
};
var __callDispose = (stack, error, hasError) => {
  var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
    return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
  };
  var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e);
  var next = (it) => {
    while (it = stack.pop()) {
      try {
        var result = it[1] && it[1].call(it[2]);
        if (it[0]) return Promise.resolve(result).then(next, (e) => (fail(e), next()));
      } catch (e) {
        fail(e);
      }
    }
    if (hasError) throw error;
  };
  return next();
};
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { MatterAggregateError } from "#MatterError.js";
import { Minutes } from "#time/TimeUnit.js";
import { Lifetime } from "#util/Lifetime.js";
import { Logger } from "../../log/Logger.js";
import { Cache } from "../../util/Cache.js";
import { asError } from "../../util/Error.js";
import { isIPv4 } from "../../util/Ip.js";
import { NoAddressAvailableError } from "../Network.js";
const logger = Logger.get("UdpMulticastServer");
class UdpMulticastServer {
  #lifetime;
  network;
  netInterface;
  #broadcastAddressIpv4;
  #broadcastAddressIpv6;
  #broadcastPort;
  #serverIpv4;
  #serverIpv6;
  static async create({
    netInterface,
    broadcastAddressIpv4,
    broadcastAddressIpv6,
    listeningPort,
    network,
    lifetime: lifetimeOwner
  }) {
    const lifetime = (lifetimeOwner || Lifetime.process)?.join("multicast server");
    try {
      let ipv4UdpChannel = void 0;
      if (broadcastAddressIpv4 !== void 0) {
        try {
          ipv4UdpChannel = await network.createUdpChannel({
            lifetime,
            type: "udp4",
            netInterface,
            listeningPort,
            reuseAddress: true
          });
          await ipv4UdpChannel.addMembership(broadcastAddressIpv4);
        } catch (error) {
          NoAddressAvailableError.accept(error);
          logger.info(
            `IPv4 UDP channel not created because IPv4 is not available: ${asError(error).message}`
          );
        }
      }
      let ipv6UdpChannel;
      try {
        ipv6UdpChannel = await network.createUdpChannel({
          lifetime,
          type: "udp6",
          netInterface,
          listeningPort,
          reuseAddress: true
        });
        await ipv6UdpChannel.addMembership(broadcastAddressIpv6);
      } catch (error) {
        NoAddressAvailableError.accept(error);
        logger.info(`IPv6 UDP interface not created because IPv6 is not available, but required my Matter`);
        throw error;
      }
      return new UdpMulticastServer(
        lifetime,
        network,
        broadcastAddressIpv4,
        broadcastAddressIpv6,
        listeningPort,
        ipv4UdpChannel,
        ipv6UdpChannel,
        netInterface
      );
    } catch (error) {
      lifetime[Symbol.dispose]();
      throw error;
    }
  }
  broadcastChannels = new Cache(
    "UDP broadcast channel",
    (netInterface, iPv4) => this.createBroadcastChannel(netInterface, iPv4),
    Minutes(5),
    async (_netInterface, channel) => (await channel).close()
  );
  constructor(lifetime, network, broadcastAddressIpv4, broadcastAddressIpv6, broadcastPort, serverIpv4, serverIpv6, netInterface) {
    this.#lifetime = lifetime;
    this.network = network;
    this.#broadcastAddressIpv4 = broadcastAddressIpv4;
    this.#broadcastAddressIpv6 = broadcastAddressIpv6;
    this.#broadcastPort = broadcastPort;
    this.#serverIpv4 = serverIpv4;
    this.#serverIpv6 = serverIpv6;
    this.netInterface = netInterface;
  }
  get supportsIpv4() {
    return this.#serverIpv4 !== void 0;
  }
  onMessage(listener) {
    this.#serverIpv4?.onData((netInterface, peerAddress, _port, message) => {
      if (netInterface === void 0) {
        return;
      }
      listener(message, peerAddress, netInterface);
    });
    this.#serverIpv6.onData((netInterface, peerAddress, _port, message) => {
      if (netInterface === void 0) {
        return;
      }
      listener(message, peerAddress, netInterface);
    });
  }
  async send(message, netInterface, uniCastTarget) {
    netInterface = netInterface ?? this.netInterface;
    if (uniCastTarget !== void 0 && netInterface !== void 0) {
      try {
        await (await this.broadcastChannels.get(netInterface, isIPv4(uniCastTarget))).send(uniCastTarget, this.#broadcastPort, message);
      } catch (error) {
        logger.info(`${netInterface} ${uniCastTarget}: ${asError(error).message}`);
      }
    } else {
      const netInterfaces = netInterface !== void 0 ? [{ name: netInterface }] : await this.network.getNetInterfaces();
      await MatterAggregateError.allSettled(
        netInterfaces.map(async ({ name: netInterface2 }) => {
          const { ipV4, ipV6 } = await this.network.getIpMac(netInterface2) ?? {
            mac: "",
            ipV4: [],
            ipV6: []
          };
          const ips = [...ipV4, ...ipV6];
          await MatterAggregateError.allSettled(
            ips.map(async (ip) => {
              const iPv4 = ipV4.includes(ip);
              const broadcastTarget = iPv4 ? this.#broadcastAddressIpv4 : this.#broadcastAddressIpv6;
              if (broadcastTarget === void 0) {
                return;
              }
              try {
                await (await this.broadcastChannels.get(netInterface2, iPv4)).send(broadcastTarget, this.#broadcastPort, message);
              } catch (error) {
                logger.info(`${netInterface2}: ${asError(error).message}`);
              }
            }),
            `Error sending UDP Multicast message on interface ${netInterface2}`
          );
        }),
        "Error sending UDP Multicast message"
      );
    }
  }
  async createBroadcastChannel(netInterface, iPv4) {
    return await this.network.createUdpChannel({
      lifetime: this.#lifetime,
      type: iPv4 ? "udp4" : "udp6",
      listeningPort: this.#broadcastPort,
      netInterface,
      reuseAddress: true
    });
  }
  async close() {
    var _stack = [];
    try {
      const _closing = __using(_stack, this.#lifetime.closing());
      await this.#serverIpv4?.close();
      await this.#serverIpv6.close();
      await this.broadcastChannels.close();
    } catch (_) {
      var _error = _, _hasError = true;
    } finally {
      __callDispose(_stack, _error, _hasError);
    }
  }
}
export {
  UdpMulticastServer
};
//# sourceMappingURL=UdpMulticastServer.js.map
