///|
// A managed Number-domain subtraction operation. The runtime owns the complete
// ToPrimitive(number) sequence; the bytecode executor only supplies operands.

///|
pub enum ExecutorCoercingSubtractionStart {
  ExecutorCoercingSubtractionCompleted(Value)
  ExecutorCoercingSubtractionSuspended(ExecutorCoercingSubtractionRequest)
}

///|
pub fn begin_executor_coercing_subtraction(
  left : Value,
  right : Value,
  loc : @token.Loc,
) -> ExecutorCoercingSubtractionStart raise Error {
  if is_js_object(left) || is_js_object(right) {
    ExecutorCoercingSubtractionSuspended(
      ExecutorCoercingSubtractionRequest(left~, right~, loc~),
    )
  } else {
    ExecutorCoercingSubtractionCompleted(finish_subtraction(left, right, loc))
  }
}

///|
priv enum ExecutorCoercingSubtractionPhase {
  ExecutorCoercingSubtractionNeedLeft
  ExecutorCoercingSubtractionAwaitLeft(ExecutorToPrimitiveOperation)
  ExecutorCoercingSubtractionHaveLeft(Double)
  ExecutorCoercingSubtractionAwaitRight(Double, ExecutorToPrimitiveOperation)
  ExecutorCoercingSubtractionComplete(Value)
}

///|
priv struct ExecutorCoercingSubtractionFrame {
  request : ExecutorCoercingSubtractionRequest
  mut phase : ExecutorCoercingSubtractionPhase
}

///|
fn ExecutorCoercingSubtractionFrame::ExecutorCoercingSubtractionFrame(
  request : ExecutorCoercingSubtractionRequest,
) -> ExecutorCoercingSubtractionFrame {
  { request, phase: ExecutorCoercingSubtractionNeedLeft, }
}

///|
fn ExecutorCoercingSubtractionFrame::finish_right(
  self : ExecutorCoercingSubtractionFrame,
  interp : Interpreter,
  left : Double,
  right : Value,
) -> ExecutorActivationStep raise Error {
  let result = finish_subtraction(
    Number(left),
    right,
    self.request.loc,
    interp=Some(interp),
  )
  self.phase = ExecutorCoercingSubtractionComplete(result)
  ExecutorActivationNormal(result)
}

///|
fn ExecutorCoercingSubtractionFrame::step_frame(
  self : ExecutorCoercingSubtractionFrame,
  interp : Interpreter,
) -> ExecutorActivationStep raise Error {
  match self.phase {
    ExecutorCoercingSubtractionNeedLeft => {
      let operation = ExecutorToPrimitiveOperation(
        self.request.left,
        ExecutorToPrimitiveNumber,
        self.request.loc,
      )
      match operation.step(interp) {
        ExecutorActivationNormal(value) => {
          let left_number = to_number(value, interp=Some(interp))
          self.phase = ExecutorCoercingSubtractionHaveLeft(left_number)
          self.step_frame(interp)
        }
        step => {
          self.phase = ExecutorCoercingSubtractionAwaitLeft(operation)
          step
        }
      }
    }
    ExecutorCoercingSubtractionAwaitLeft(operation) =>
      match operation.step(interp) {
        ExecutorActivationNormal(value) => {
          let left_number = to_number(value, interp=Some(interp))
          self.phase = ExecutorCoercingSubtractionHaveLeft(left_number)
          self.step_frame(interp)
        }
        step => {
          self.phase = ExecutorCoercingSubtractionAwaitLeft(operation)
          step
        }
      }
    ExecutorCoercingSubtractionHaveLeft(left_number) => {
      let operation = ExecutorToPrimitiveOperation(
        self.request.right,
        ExecutorToPrimitiveNumber,
        self.request.loc,
      )
      match operation.step(interp) {
        ExecutorActivationNormal(value) =>
          self.finish_right(interp, left_number, value)
        step => {
          self.phase = ExecutorCoercingSubtractionAwaitRight(
            left_number, operation,
          )
          step
        }
      }
    }
    ExecutorCoercingSubtractionAwaitRight(left_number, operation) =>
      match operation.step(interp) {
        ExecutorActivationNormal(value) =>
          self.finish_right(interp, left_number, value)
        step => {
          self.phase = ExecutorCoercingSubtractionAwaitRight(
            left_number, operation,
          )
          step
        }
      }
    ExecutorCoercingSubtractionComplete(value) => {
      ignore(value)
      raise @errors.InternalError(
        message="coercing subtraction frame stepped after completion",
      )
    }
  }
}

///|
impl ExecutorActivationFrame for ExecutorCoercingSubtractionFrame with fn step(
  self,
  interp,
) {
  self.step_frame(interp)
}

///|
impl ExecutorActivationFrame for ExecutorCoercingSubtractionFrame with fn deliver_activation_completion(
  self,
  completion,
) {
  match self.phase {
    ExecutorCoercingSubtractionAwaitLeft(operation)
    | ExecutorCoercingSubtractionAwaitRight(_, operation) =>
      operation.deliver_activation_completion(completion)
    ExecutorCoercingSubtractionNeedLeft
    | ExecutorCoercingSubtractionHaveLeft(_)
    | ExecutorCoercingSubtractionComplete(_) =>
      raise @errors.InternalError(
        message="coercing subtraction received an unexpected child completion",
      )
  }
}