import { queueTask } from "./scheduling.js";
import { intersection } from "./intersection.js";
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-database
class Database {
  transactions = [];
  rawObjectStores = new Map();
  connections = [];
  constructor(name, version) {
    this.name = name;
    this.version = version;
    this.processTransactions = this.processTransactions.bind(this);
  }
  processTransactions() {
    queueTask(() => {
      const running = this.transactions.filter(transaction => transaction._started && transaction._state !== "finished");
      const waiting = this.transactions.filter(transaction => !transaction._started && transaction._state !== "finished");

      // The next transaction to run is the first waiting one that doesn't overlap with either a running one or a
      // preceding waiting one. This allows non-overlapping transactions to run in parallel.
      // The exception is readonly transactions, which are allowed to run in parallel with other readonly
      // transactions, even with overlapping scopes, since no data is being modified.
      const next = waiting.find((transaction, i) => {
        const anyRunning = running.some(other => !(transaction.mode === "readonly" && other.mode === "readonly") && intersection(other._scope, transaction._scope).size > 0);
        if (anyRunning) {
          return false;
        }

        // If any _preceding_ waiting transactions are blocked, then that's also blocking.
        // E.g. if you have 3 transactions: [a], [a,b], and [b,c], then [a] blocks [a,b] which blocks [b,c]
        // until [a] is complete, even though [a] and [b,c] share no overlap.
        // Note that readonly transactions do not have to be handled as a special case here,
        // because if any transactions with overlapping scopes are blocked, then we can assume they are
        const anyWaiting = waiting.slice(0, i).some(other => intersection(other._scope, transaction._scope).size > 0);
        return !anyWaiting;
      });
      if (next) {
        next.addEventListener("complete", this.processTransactions);
        next.addEventListener("abort", this.processTransactions);
        next._start();
      }
    });
  }
}
export default Database;