"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
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 Computed_exports = {};
__export(Computed_exports, {
  _Computed: () => _Computed,
  computed: () => computed,
  getComputedInstance: () => getComputedInstance,
  isComputed: () => isComputed,
  isUninitialized: () => isUninitialized,
  withDiff: () => withDiff
});
module.exports = __toCommonJS(Computed_exports);
var import_ArraySet = require("./ArraySet");
var import_HistoryBuffer = require("./HistoryBuffer");
var import_capture = require("./capture");
var import_constants = require("./constants");
var import_helpers = require("./helpers");
var import_transactions = require("./transactions");
var import_types = require("./types");
const UNINITIALIZED = Symbol("UNINITIALIZED");
const isUninitialized = (value) => {
  return value === UNINITIALIZED;
};
class WithDiff {
  constructor(value, diff) {
    this.value = value;
    this.diff = diff;
  }
}
function withDiff(value, diff) {
  return new WithDiff(value, diff);
}
class _Computed {
  constructor(name, derive, options) {
    this.name = name;
    this.derive = derive;
    if (options?.historyLength) {
      this.historyBuffer = new import_HistoryBuffer.HistoryBuffer(options.historyLength);
    }
    this.computeDiff = options?.computeDiff;
    this.isEqual = options?.isEqual ?? import_helpers.equals;
  }
  lastChangedEpoch = import_constants.GLOBAL_START_EPOCH;
  lastTraversedEpoch = import_constants.GLOBAL_START_EPOCH;
  /**
   * The epoch when the reactor was last checked.
   */
  lastCheckedEpoch = import_constants.GLOBAL_START_EPOCH;
  parents = [];
  parentEpochs = [];
  children = new import_ArraySet.ArraySet();
  get isActivelyListening() {
    return !this.children.isEmpty;
  }
  historyBuffer;
  // The last-computed value of this signal.
  state = UNINITIALIZED;
  computeDiff;
  isEqual;
  __unsafe__getWithoutCapture() {
    const isNew = this.lastChangedEpoch === import_constants.GLOBAL_START_EPOCH;
    if (!isNew && (this.lastCheckedEpoch === import_transactions.globalEpoch || !(0, import_helpers.haveParentsChanged)(this))) {
      this.lastCheckedEpoch = import_transactions.globalEpoch;
      return this.state;
    }
    try {
      (0, import_capture.startCapturingParents)(this);
      const result = this.derive(this.state, this.lastCheckedEpoch);
      const newState = result instanceof WithDiff ? result.value : result;
      if (this.state === UNINITIALIZED || !this.isEqual(newState, this.state)) {
        if (this.historyBuffer && !isNew) {
          const diff = result instanceof WithDiff ? result.diff : void 0;
          this.historyBuffer.pushEntry(
            this.lastChangedEpoch,
            import_transactions.globalEpoch,
            diff ?? this.computeDiff?.(this.state, newState, this.lastCheckedEpoch, import_transactions.globalEpoch) ?? import_types.RESET_VALUE
          );
        }
        this.lastChangedEpoch = import_transactions.globalEpoch;
        this.state = newState;
      }
      this.lastCheckedEpoch = import_transactions.globalEpoch;
      return this.state;
    } finally {
      (0, import_capture.stopCapturingParents)();
    }
  }
  get value() {
    const value = this.__unsafe__getWithoutCapture();
    (0, import_capture.maybeCaptureParent)(this);
    return value;
  }
  getDiffSince(epoch) {
    this.value;
    if (epoch >= this.lastChangedEpoch) {
      return import_helpers.EMPTY_ARRAY;
    }
    return this.historyBuffer?.getChangesSince(epoch) ?? import_types.RESET_VALUE;
  }
}
function computedAnnotation(options = {}, _target, key, descriptor) {
  const originalMethod = descriptor.get;
  const derivationKey = Symbol.for("__@tldraw/state__computed__" + key);
  descriptor.get = function() {
    let d = this[derivationKey];
    if (!d) {
      d = new _Computed(key, originalMethod.bind(this), options);
      Object.defineProperty(this, derivationKey, {
        enumerable: false,
        configurable: false,
        writable: false,
        value: d
      });
    }
    return d.value;
  };
  return descriptor;
}
function getComputedInstance(obj, propertyName) {
  const key = Symbol.for("__@tldraw/state__computed__" + propertyName.toString());
  let inst = obj[key];
  if (!inst) {
    obj[propertyName];
    inst = obj[key];
  }
  return inst;
}
function computed() {
  if (arguments.length === 1) {
    const options = arguments[0];
    return (target, key, descriptor) => computedAnnotation(options, target, key, descriptor);
  } else if (typeof arguments[0] === "string") {
    return new _Computed(arguments[0], arguments[1], arguments[2]);
  } else {
    return computedAnnotation(void 0, arguments[0], arguments[1], arguments[2]);
  }
}
function isComputed(value) {
  return value && value instanceof _Computed;
}
//# sourceMappingURL=Computed.js.map
