///|
// Managed ToNumber for an already-evaluated value. Primitive values complete
// immediately; object conversion remains resumable because user-defined
// Symbol.toPrimitive, valueOf, or toString methods may execute guest code.
pub struct ExecutorNumericConversionRequest {
  priv value : Value
  priv loc : @token.Loc
}

///|
fn ExecutorNumericConversionRequest::ExecutorNumericConversionRequest(
  value~ : Value,
  loc~ : @token.Loc,
) -> ExecutorNumericConversionRequest {
  { value, loc, }
}

///|
pub enum ExecutorNumericConversionStart {
  ExecutorNumericConversionCompleted(Value)
  ExecutorNumericConversionSuspended(ExecutorNumericConversionRequest)
}

///|
pub fn begin_executor_numeric_conversion(
  interp : Interpreter,
  value : Value,
  loc : @token.Loc,
) -> ExecutorNumericConversionStart raise Error {
  if is_js_object(value) {
    ExecutorNumericConversionSuspended(
      ExecutorNumericConversionRequest(value~, loc~),
    )
  } else {
    ExecutorNumericConversionCompleted(
      Number(to_number(value, interp=Some(interp))),
    )
  }
}

///|
pub fn executor_activation_numeric_conversion_request(
  request : ExecutorNumericConversionRequest,
) -> ExecutorActivationStep {
  ExecutorActivationNumericConversion(request)
}

///|
fn executor_numeric_conversion_completion(
  step : ExecutorCompletedStep,
) -> ExecutorActivationCompletion {
  match step {
    ExecutorCompletedNormal(value) | ExecutorCompletedReturn(value) =>
      ExecutorActivationCompletionNormal(value)
    ExecutorCompletedReference(_) =>
      ExecutorActivationCompletionAbrupt(
        @errors.InternalError(
          message="numeric conversion completed with a pending activation",
        ),
      )
  }
}

///|
priv enum ExecutorNumericConversionPhase {
  ExecutorNumericConversionNeedInput
  ExecutorNumericConversionAwaitToPrimitive(ExecutorToPrimitiveOperation)
  ExecutorNumericConversionComplete
}

///|
priv struct ExecutorNumericConversionFrame {
  request : ExecutorNumericConversionRequest
  mut phase : ExecutorNumericConversionPhase
}

///|
fn ExecutorNumericConversionFrame::ExecutorNumericConversionFrame(
  request : ExecutorNumericConversionRequest,
) -> ExecutorNumericConversionFrame {
  { request, phase: ExecutorNumericConversionNeedInput, }
}

///|
fn ExecutorNumericConversionFrame::finish(
  self : ExecutorNumericConversionFrame,
  interp : Interpreter,
  primitive : Value,
) -> ExecutorActivationStep raise Error {
  self.phase = ExecutorNumericConversionComplete
  ExecutorActivationNormal(Number(to_number(primitive, interp=Some(interp))))
}

///|
impl ExecutorActivationFrame for ExecutorNumericConversionFrame with fn step(
  self,
  interp,
) {
  match self.phase {
    ExecutorNumericConversionNeedInput => {
      let operation = ExecutorToPrimitiveOperation(
        self.request.value,
        ExecutorToPrimitiveNumber,
        self.request.loc,
      )
      match operation.step(interp) {
        ExecutorActivationNormal(value) => self.finish(interp, value)
        step => {
          self.phase = ExecutorNumericConversionAwaitToPrimitive(operation)
          step
        }
      }
    }
    ExecutorNumericConversionAwaitToPrimitive(operation) =>
      match operation.step(interp) {
        ExecutorActivationNormal(value) => self.finish(interp, value)
        step => {
          self.phase = ExecutorNumericConversionAwaitToPrimitive(operation)
          step
        }
      }
    ExecutorNumericConversionComplete =>
      raise @errors.InternalError(
        message="numeric conversion frame stepped after completion",
      )
  }
}

///|
impl ExecutorActivationFrame for ExecutorNumericConversionFrame with fn deliver_activation_completion(
  self,
  completion,
) {
  match self.phase {
    ExecutorNumericConversionAwaitToPrimitive(operation) =>
      operation.deliver_activation_completion(completion)
    ExecutorNumericConversionNeedInput | ExecutorNumericConversionComplete =>
      raise @errors.InternalError(
        message="numeric conversion received an unexpected child completion",
      )
  }
}