//! Control-flow instruction execution.

use crate::coroutine::BlockReason;
use crate::coroutine::{Fault, Value};
use crate::engine::{CoroUpdate, ProtocolMachine, StepPack};
use crate::exec::helpers::{empty_pack, write_reg_pack};
use crate::instr::{Endpoint, ImmValue, PC};

/// Execute `set`.
#[must_use]
pub(crate) fn step_set(dst: u16, val: ImmValue) -> StepPack {
    let value = match val {
        ImmValue::Unit => Value::Unit,
        ImmValue::Nat(n) => Value::Nat(n),
        ImmValue::Bool(b) => Value::Bool(b),
        ImmValue::Str(s) => Value::Str(s),
    };
    write_reg_pack(dst, value)
}

/// Execute `move`.
pub(crate) fn step_move(
    machine: &ProtocolMachine,
    coro_idx: usize,
    dst: u16,
    src: u16,
) -> Result<StepPack, Fault> {
    let value = machine.read_reg(coro_idx, src)?;
    Ok(write_reg_pack(dst, value))
}

/// Execute `jump`.
#[must_use]
pub(crate) fn step_jump(target: PC) -> StepPack {
    empty_pack(CoroUpdate::SetPc(target))
}

/// Execute `yield`.
#[must_use]
pub(crate) fn step_yield() -> StepPack {
    empty_pack(CoroUpdate::AdvancePcBlock(BlockReason::Spawn))
}

/// Execute `halt`.
pub(crate) fn step_halt(machine: &ProtocolMachine, ep: &Endpoint) -> Result<StepPack, Fault> {
    machine.step_halt(ep)
}

/// Execute `spawn`.
pub(crate) fn step_spawn(
    machine: &mut ProtocolMachine,
    coro_idx: usize,
    target: PC,
    args: &[u16],
) -> Result<StepPack, Fault> {
    machine.step_spawn(coro_idx, target, args)
}
