import { once } from "./functional.mjs";
import { Iterable } from "./iterator.mjs";
function trackDisposable(x) {
  return x;
}
function setParentOfDisposable(child, parent) {
}
function markAsSingleton(singleton) {
  return singleton;
}
class MultiDisposeError extends Error {
  constructor(errors) {
    super(`Encountered errors while disposing of store. Errors: [${errors.join(", ")}]`);
    this.errors = errors;
  }
}
function isDisposable(thing) {
  return typeof thing.dispose === "function" && thing.dispose.length === 0;
}
function dispose(arg) {
  if (Iterable.is(arg)) {
    let errors = [];
    for (const d of arg) {
      if (d) {
        try {
          d.dispose();
        } catch (e) {
          errors.push(e);
        }
      }
    }
    if (errors.length === 1) {
      throw errors[0];
    } else if (errors.length > 1) {
      throw new MultiDisposeError(errors);
    }
    return Array.isArray(arg) ? [] : arg;
  } else if (arg) {
    arg.dispose();
    return arg;
  }
}
function combinedDisposable(...disposables) {
  const parent = toDisposable(() => dispose(disposables));
  return parent;
}
function toDisposable(fn) {
  const self = trackDisposable({
    dispose: once(() => {
      fn();
    })
  });
  return self;
}
class DisposableStore {
  constructor() {
    this._toDispose = /* @__PURE__ */ new Set();
    this._isDisposed = false;
  }
  /**
   * Dispose of all registered disposables and mark this object as disposed.
   *
   * Any future disposables added to this object will be disposed of on `add`.
   */
  dispose() {
    if (this._isDisposed) {
      return;
    }
    this._isDisposed = true;
    this.clear();
  }
  /**
   * Returns `true` if this object has been disposed
   */
  get isDisposed() {
    return this._isDisposed;
  }
  /**
   * Dispose of all registered disposables but do not mark this object as disposed.
   */
  clear() {
    try {
      dispose(this._toDispose.values());
    } finally {
      this._toDispose.clear();
    }
  }
  add(o) {
    if (!o) {
      return o;
    }
    if (o === this) {
      throw new Error("Cannot register a disposable on itself!");
    }
    if (this._isDisposed) {
      if (!DisposableStore.DISABLE_DISPOSED_WARNING) {
        console.warn(new Error("Trying to add a disposable to a DisposableStore that has already been disposed of. The added object will be leaked!").stack);
      }
    } else {
      this._toDispose.add(o);
    }
    return o;
  }
}
DisposableStore.DISABLE_DISPOSED_WARNING = false;
class Disposable {
  constructor() {
    this._store = new DisposableStore();
    setParentOfDisposable(this._store);
  }
  dispose() {
    this._store.dispose();
  }
  _register(o) {
    if (o === this) {
      throw new Error("Cannot register a disposable on itself!");
    }
    return this._store.add(o);
  }
}
Disposable.None = Object.freeze({ dispose() {
} });
class MutableDisposable {
  constructor() {
    this._isDisposed = false;
  }
  get value() {
    return this._isDisposed ? void 0 : this._value;
  }
  set value(value) {
    var _a;
    if (this._isDisposed || value === this._value) {
      return;
    }
    (_a = this._value) === null || _a === void 0 ? void 0 : _a.dispose();
    this._value = value;
  }
  clear() {
    this.value = void 0;
  }
  dispose() {
    var _a;
    this._isDisposed = true;
    (_a = this._value) === null || _a === void 0 ? void 0 : _a.dispose();
    this._value = void 0;
  }
  /**
   * Clears the value, but does not dispose it.
   * The old value is returned.
  */
  clearAndLeak() {
    const oldValue = this._value;
    this._value = void 0;
    return oldValue;
  }
}
class SafeDisposable {
  constructor() {
    this.dispose = () => {
    };
    this.unset = () => {
    };
    this.isset = () => false;
  }
  set(fn) {
    let callback = fn;
    this.unset = () => callback = void 0;
    this.isset = () => callback !== void 0;
    this.dispose = () => {
      if (callback) {
        callback();
        callback = void 0;
      }
    };
    return this;
  }
}
class ImmortalReference {
  constructor(object) {
    this.object = object;
  }
  dispose() {
  }
}
export {
  Disposable,
  DisposableStore,
  ImmortalReference,
  MultiDisposeError,
  MutableDisposable,
  SafeDisposable,
  combinedDisposable,
  dispose,
  isDisposable,
  markAsSingleton,
  toDisposable
};
