pub type VM_Word = i64;

#[derive(Clone, Copy, Debug)]
#[repr(u64)]
pub enum VM_Instruction {
    Nop, Jmp, Call, Branch, Ret,
    Lit, Dup, Drop, Swap, Rot,
    Fetch, Store,
    Accept, Emit,
    Add, //Sub, Mul, Div, Rem,
    Addf,//Subf, Mulf, Divf
    BitwiseNot,
}

pub struct VM {
    pub data_stack: Vec< VM_Word >,
    pub call_stack: Vec< VM_Word >,

    pub inst_ptr: VM_Word,
    pub memory: Vec< VM_Word >,
}

impl VM {
    pub fn new(memsize: usize) -> Self {
        VM {
            data_stack: Vec::new(),
            call_stack: Vec::new(),

            inst_ptr: 0,
            memory: vec![0; memsize]
        }
    }

    pub fn execute(&mut self, entry: VM_Word) {
        self.inst_ptr = entry;
        while self.execute_step() {}
    }

    pub fn execute_step(&mut self) -> bool {
        // fetch instruction from memory
        let inst = unsafe { std::mem::transmute(self.memory[ self.inst_ptr as usize ]) };
        self.inst_ptr += 1;

        // decode instruction & load immediate if neccesary
        let mut imm : VM_Word = 0;
        match inst {  
            VM_Instruction::Jmp |
            VM_Instruction::Call |
            VM_Instruction::Branch |
            VM_Instruction::Lit => {
                imm = self.memory[ self.inst_ptr as usize ];
                self.inst_ptr += 1;
            }
            _ => {}
        }

        // execute instruction
        self.execute_instruction( inst, imm )
    }

    pub fn execute_instruction(&mut self, inst: VM_Instruction, imm: VM_Word) -> bool {
        // eprintln!("execute {:?}, {}", inst, imm);
        match inst {
            VM_Instruction::Nop => {}
            VM_Instruction::Lit => {
                self.data_stack.push( imm );
            }
            VM_Instruction::Jmp => {
                self.inst_ptr += imm;
            }
            VM_Instruction::Call => {
                self.call_stack.push( self.inst_ptr );
                self.inst_ptr = imm;
            }
            VM_Instruction::Branch => {
                if self.data_stack.pop().expect("branch expects val") == 0 {
                    //self.call_stack.push( self.inst_ptr );
                    self.inst_ptr += imm;
                }
            }
            VM_Instruction::Ret => {
                if let Some(target) = self.call_stack.pop() {
                    self.inst_ptr = target;
                } else {
                    return false;
                }
            }
            VM_Instruction::Fetch => {
                let addr = self.data_stack.pop().expect("fetch requries addr");
                let val = self.memory[ addr as usize ];
                self.data_stack.push(val);
            }
            VM_Instruction::Store => {
                let addr = self.data_stack.pop().expect("store requires addr");
                let val = self.data_stack.pop().expect("store requires val");
                self.memory[ addr as usize ] = val;
            }
            VM_Instruction::Accept => {
                // todo: read word from stdin
                self.data_stack.push(0);
            }
            VM_Instruction::Emit => {
                let val = char::from(self.data_stack.pop().expect("emit expects value") as u8);
                print!("{}", val);
            }
            VM_Instruction::Dup => {
                self.data_stack.push( *self.data_stack.last().expect("dup expect val") );
            }
            VM_Instruction::Drop => {
                self.data_stack.pop().expect("drop expects value");
            }
            VM_Instruction::Swap => {
                let a = self.data_stack.pop().expect("swap expects value");
                let b = self.data_stack.pop().expect("swap expects value");
                self.data_stack.push(a);
                self.data_stack.push(b);
            }            
            VM_Instruction::Rot => {
                let a = self.data_stack.pop().expect("rot expects value");
                let b = self.data_stack.pop().expect("rot expects value");
                let c = self.data_stack.pop().expect("rot expects value");
                self.data_stack.push(a);
                self.data_stack.push(c);
                self.data_stack.push(b);
            }
            VM_Instruction::Add => {
                let a = self.data_stack.pop().expect("add expects value");
                let b = self.data_stack.pop().expect("add expects value");
                self.data_stack.push( a + b );
            }
            VM_Instruction::Addf => {
                let a = self.data_stack.pop().expect("addf expects value");
                let af : f64 = unsafe{ std::mem::transmute(a) };

                let b = self.data_stack.pop().expect("addf expects value");
                let bf : f64 = unsafe{ std::mem::transmute(a) };

                let cf = af + bf;
                let c : i64 = unsafe{ std::mem::transmute(cf) };

                self.data_stack.push(c);
            }
            VM_Instruction::BitwiseNot => {
                let a = self.data_stack.pop().expect("bitwise not expects value");
                self.data_stack.push( !a );
            }
        }

        true
    }
}