///| IEC 61131-3 指令表 (Instruction List, IL) 虚拟机与字节码执行引擎

///|
/// IL 指令操作码定义
pub(all) enum IlOpcode {
  Ld // 加载到累加器
  Ldn // 取反后加载到累加器
  St // 累加器保存至变量
  And // 逻辑与
  Andn // 逻辑与非
  Or // 逻辑或
  Orn // 逻辑或非
  Xor // 逻辑异或
  Add // 算术加
  Sub // 算术减
  Mul // 算术乘
  Div // 算术除
  Mod // 取模
  Eq // 等于比较
  Ne // 不等于比较
  Gt // 大于比较
  Lt // 小于比较
  Ge // 大于等于比较
  Le // 小于等于比较
  Jmp(Int) // 无条件跳转到指令索引
  Jmpc(Int) // 条件跳转 (累加器为 TRUE 时)
  Jmpcn(Int) // 条件跳转 (累加器为 FALSE 时)
  Halt // 停机
} derive(Debug, Eq)

///|
/// 单条 IL 虚拟机指令
pub(all) struct IlInstruction {
  opcode : IlOpcode
  operand_val : PlcValue?
  operand_var : String?
} derive(Debug)

///|
pub fn IlInstruction::new(op : IlOpcode) -> IlInstruction {
  { opcode: op, operand_val: None, operand_var: None, }
}

///|
pub fn IlInstruction::with_val(op : IlOpcode, val : PlcValue) -> IlInstruction {
  { opcode: op, operand_val: Some(val), operand_var: None, }
}

///|
pub fn IlInstruction::with_var(
  op : IlOpcode,
  var_name : String,
) -> IlInstruction {
  { opcode: op, operand_val: None, operand_var: Some(var_name), }
}

///|
/// 虚拟机状态
pub(all) struct PlcVm {
  mut instructions : Array[IlInstruction]
  mem : PlcMemory
  mut pc : Int
  mut acc : PlcValue // Current Result (CR) 累加器
  mut halted : Bool
} derive(Debug)

///|
pub fn PlcVm::new(mem : PlcMemory) -> PlcVm {
  { instructions: [], mem, pc: 0, acc: PlcValue::Bool(false), halted: false, }
}

///|
pub fn PlcVm::load_instructions(
  self : PlcVm,
  instrs : Array[IlInstruction],
) -> Unit {
  self.instructions = instrs
  self.pc = 0
  self.halted = false
}

///|
/// 重置虚拟机状态 (用于下一个扫描周期重新从头执行)
pub fn PlcVm::reset(self : PlcVm) -> Unit {
  self.pc = 0
  self.acc = PlcValue::Bool(false)
  self.halted = false
}

///|
/// 获取操作数的值 (字面量或从内存读取)
fn PlcVm::fetch_operand(self : PlcVm, inst : IlInstruction) -> PlcValue {
  match inst.operand_val {
    Some(v) => v
    None =>
      match inst.operand_var {
        Some(name) => self.mem.get_var(name)
        None => PlcValue::Null
      }
  }
}

///|
/// 单步执行一条指令
pub fn PlcVm::step(self : PlcVm) -> Bool {
  if self.halted || self.pc < 0 || self.pc >= self.instructions.length() {
    self.halted = true
    return false
  }

  let inst = self.instructions[self.pc]
  self.pc = self.pc + 1

  match inst.opcode {
    IlOpcode::Ld => self.acc = self.fetch_operand(inst)
    IlOpcode::Ldn => {
      let v = self.fetch_operand(inst)
      self.acc = PlcValue::Bool(!v.is_true())
    }
    IlOpcode::St =>
      match inst.operand_var {
        Some(name) => self.mem.set_var(name, self.acc)
        None => ()
      }
    IlOpcode::And => {
      let v = self.fetch_operand(inst)
      self.acc = PlcValue::Bool(self.acc.is_true() && v.is_true())
    }
    IlOpcode::Andn => {
      let v = self.fetch_operand(inst)
      self.acc = PlcValue::Bool(self.acc.is_true() && !v.is_true())
    }
    IlOpcode::Or => {
      let v = self.fetch_operand(inst)
      self.acc = PlcValue::Bool(self.acc.is_true() || v.is_true())
    }
    IlOpcode::Orn => {
      let v = self.fetch_operand(inst)
      self.acc = PlcValue::Bool(self.acc.is_true() || !v.is_true())
    }
    IlOpcode::Xor => {
      let v = self.fetch_operand(inst)
      self.acc = PlcValue::Bool(self.acc.is_true() != v.is_true())
    }
    IlOpcode::Add => {
      let v = self.fetch_operand(inst)
      self.acc = self.acc.add(v)
    }
    IlOpcode::Sub => {
      let v = self.fetch_operand(inst)
      self.acc = self.acc.sub(v)
    }
    IlOpcode::Mul => {
      let v = self.fetch_operand(inst)
      self.acc = self.acc.mul(v)
    }
    IlOpcode::Div => {
      let v = self.fetch_operand(inst)
      self.acc = self.acc.div(v)
    }
    IlOpcode::Mod => {
      let v = self.fetch_operand(inst)
      self.acc = self.acc.modulo(v)
    }
    IlOpcode::Eq => {
      let v = self.fetch_operand(inst)
      self.acc = PlcValue::Bool(self.acc.eq(v))
    }
    IlOpcode::Ne => {
      let v = self.fetch_operand(inst)
      self.acc = PlcValue::Bool(!self.acc.eq(v))
    }
    IlOpcode::Gt => {
      let v = self.fetch_operand(inst)
      self.acc = PlcValue::Bool(!self.acc.lte(v))
    }
    IlOpcode::Lt => {
      let v = self.fetch_operand(inst)
      self.acc = PlcValue::Bool(self.acc.lt(v))
    }
    IlOpcode::Ge => {
      let v = self.fetch_operand(inst)
      self.acc = PlcValue::Bool(!self.acc.lt(v))
    }
    IlOpcode::Le => {
      let v = self.fetch_operand(inst)
      self.acc = PlcValue::Bool(self.acc.lte(v))
    }
    IlOpcode::Jmp(target) => self.pc = target
    IlOpcode::Jmpc(target) => if self.acc.is_true() { self.pc = target }
    IlOpcode::Jmpcn(target) => if !self.acc.is_true() { self.pc = target }
    IlOpcode::Halt => {
      self.halted = true
      return false
    }
  }

  true
}

///|
/// 连续执行指令序列直到停机或达到最大周期指令上限
pub fn PlcVm::run(self : PlcVm, max_steps : Int) -> Unit {
  let mut steps = 0
  while !self.halted && steps < max_steps {
    let continued = self.step()
    if !continued {
      break
    }
    steps = steps + 1
  }
}