"use strict"
var __createBinding =
  (this && this.__createBinding) ||
  (Object.create
    ? function (o, m, k, k2) {
        if (k2 === undefined) k2 = k
        Object.defineProperty(o, k2, {
          enumerable: true,
          get: function () {
            return m[k]
          }
        })
      }
    : function (o, m, k, k2) {
        if (k2 === undefined) k2 = k
        o[k2] = m[k]
      })
var __setModuleDefault =
  (this && this.__setModuleDefault) ||
  (Object.create
    ? function (o, v) {
        Object.defineProperty(o, "default", { enumerable: true, value: v })
      }
    : function (o, v) {
        o["default"] = v
      })
var __importStar =
  (this && this.__importStar) ||
  function (mod) {
    if (mod && mod.__esModule) return mod
    var result = {}
    if (mod != null)
      for (var k in mod)
        if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k))
          __createBinding(result, mod, k)
    __setModuleDefault(result, mod)
    return result
  }
Object.defineProperty(exports, "__esModule", { value: true })
exports.STMDriver = void 0
const tracing_1 = __importStar(require("../../../Tracing"))
const fileName_1 = "(@effect-ts/system): _src/Transactional/STM/_internal/driver.ts"
const Stack_1 = require("../../../Stack")
const TExit = __importStar(require("../TExit"))
const STM = __importStar(require("./primitives"))
class STMDriver {
  constructor(self, journal, fiberId, r0) {
    this.self = self
    this.journal = journal
    this.fiberId = fiberId
    this.envStack = new Stack_1.Stack(r0)
  }
  unwindStack(error, isRetry) {
    let result = undefined
    while (this.contStack && !result) {
      const cont = this.contStack.value
      this.contStack = this.contStack.previous
      if (cont._typeId === STM.STMOnFailureTypeId) {
        if (!isRetry) {
          result = cont.onFailure(error)
        }
      }
      if (cont._typeId === STM.STMOnRetryTypeId) {
        if (isRetry) {
          result = cont.onRetry
        }
      }
    }
    return result
  }
  run() {
    let curr = this.self
    let exit = undefined
    while (!exit && curr) {
      const k = curr
      switch (k._typeId) {
        case STM.STMSucceedTypeId: {
          const a = k.a()
          if (!this.contStack) {
            exit = TExit.succeed(a)
          } else {
            const cont = this.contStack.value
            this.contStack = this.contStack.previous
            curr = cont.apply(a)
          }
          break
        }
        case STM.STMSucceedNowTypeId: {
          const a = k.a
          if (!this.contStack) {
            exit = TExit.succeed(a)
          } else {
            const cont = this.contStack.value
            this.contStack = this.contStack.previous
            curr = cont.apply(a)
          }
          break
        }
        case STM.STMProvideSomeTypeId: {
          this.envStack = new Stack_1.Stack(k.f(this.envStack.value), this.envStack)
          curr = STM.ensuring_(
            k.stm,
            STM.succeedWith(() => {
              this.envStack = this.envStack.previous
            })
          )
          break
        }
        case STM.STMOnRetryTypeId: {
          this.contStack = new Stack_1.Stack(k, this.contStack)
          curr = k.stm
          break
        }
        case STM.STMOnFailureTypeId: {
          this.contStack = new Stack_1.Stack(k, this.contStack)
          curr = k.stm
          break
        }
        case STM.STMOnSuccessTypeId: {
          this.contStack = new Stack_1.Stack(k, this.contStack)
          curr = k.stm
          break
        }
        case STM.STMEffectTypeId: {
          try {
            const a = k.f(this.journal, this.fiberId, this.envStack.value)
            if (!this.contStack) {
              exit = TExit.succeed(a)
            } else {
              const cont = this.contStack.value
              this.contStack = this.contStack.previous
              curr = cont.apply(a)
            }
          } catch (e) {
            if (STM.isFailException(e)) {
              curr = this.unwindStack(e.e, false)
              if (!curr) {
                exit = TExit.fail(e.e)
              }
            } else if (STM.isRetryException(e)) {
              curr = this.unwindStack(undefined, true)
              if (!curr) {
                exit = TExit.retry
              }
            } else if (STM.isDieException(e)) {
              curr = this.unwindStack(e.e, false)
              if (!curr) {
                exit = TExit.die(e.e)
              }
            } else {
              throw e
            }
          }
        }
      }
    }
    return exit
  }
}
exports.STMDriver = STMDriver
//# sourceMappingURL=driver.js.map
