"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
var __typeError = (msg) => {
  throw TypeError(msg);
};
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
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();
};
var UdpMulticastServer_exports = {};
__export(UdpMulticastServer_exports, {
  UdpMulticastServer: () => UdpMulticastServer
});
module.exports = __toCommonJS(UdpMulticastServer_exports);
var import_MatterError = require("#MatterError.js");
var import_TimeUnit = require("#time/TimeUnit.js");
var import_Lifetime = require("#util/Lifetime.js");
var import_Logger = require("../../log/Logger.js");
var import_Cache = require("../../util/Cache.js");
var import_Error = require("../../util/Error.js");
var import_Ip = require("../../util/Ip.js");
var import_Network = require("../Network.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_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 || import_Lifetime.Lifetime.process)?.join("multicast server");
    try {
      let ipv4UdpSocket = void 0;
      if (broadcastAddressIpv4 !== void 0) {
        try {
          ipv4UdpSocket = await network.createUdpSocket({
            lifetime,
            type: "udp4",
            netInterface,
            listeningPort,
            reuseAddress: true
          });
          await ipv4UdpSocket.addMembership(broadcastAddressIpv4);
        } catch (error) {
          import_Network.NoAddressAvailableError.accept(error);
          logger.info(
            `IPv4 UDP channel not created because IPv4 is not available: ${(0, import_Error.asError)(error).message}`
          );
        }
      }
      let ipv6UdpSocket;
      try {
        ipv6UdpSocket = await network.createUdpSocket({
          lifetime,
          type: "udp6",
          netInterface,
          listeningPort,
          reuseAddress: true
        });
        await ipv6UdpSocket.addMembership(broadcastAddressIpv6);
      } catch (error) {
        import_Network.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,
        ipv4UdpSocket,
        ipv6UdpSocket,
        netInterface
      );
    } catch (error) {
      lifetime[Symbol.dispose]();
      throw error;
    }
  }
  broadcastChannels = new import_Cache.Cache(
    "UDP broadcast channel",
    (netInterface, isIPv42) => this.createBroadcastChannel(netInterface, isIPv42),
    (0, import_TimeUnit.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, (0, import_Ip.isIPv4)(uniCastTarget))).send(uniCastTarget, this.#broadcastPort, message);
      } catch (error) {
        logger.info(`${netInterface} ${uniCastTarget}: ${(0, import_Error.asError)(error).message}`);
      }
    } else {
      const netInterfaces = netInterface !== void 0 ? [{ name: netInterface }] : await this.network.getNetInterfaces();
      await import_MatterError.MatterAggregateError.allSettled(
        netInterfaces.map(async ({ name: netInterface2 }) => {
          const { ipV4, ipV6 } = await this.network.getIpMac(netInterface2) ?? {
            mac: "",
            ipV4: [],
            ipV6: []
          };
          const targets = new Array();
          if (ipV4.length > 0 && this.#broadcastAddressIpv4 !== void 0) {
            targets.push({ isIPv4: true, address: this.#broadcastAddressIpv4 });
          }
          if (ipV6.length > 0) {
            targets.push({ isIPv4: false, address: this.#broadcastAddressIpv6 });
          }
          await import_MatterError.MatterAggregateError.allSettled(
            targets.map(async ({ isIPv4: isIPv42, address }) => {
              try {
                await (await this.broadcastChannels.get(netInterface2, isIPv42)).send(address, this.#broadcastPort, message);
              } catch (error) {
                logger.info(`${netInterface2}: ${(0, import_Error.asError)(error).message}`);
              }
            }),
            `Error sending UDP Multicast message on interface ${netInterface2}`
          );
        }),
        "Error sending UDP Multicast message"
      );
    }
  }
  async createBroadcastChannel(netInterface, isIPv42) {
    return await this.network.createUdpSocket({
      lifetime: this.#lifetime,
      type: isIPv42 ? "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);
    }
  }
}
//# sourceMappingURL=UdpMulticastServer.js.map
