enum Op {
    Inc(v),
    Move(v),
    Loop(ops),
    Input,
    Print,
}

struct Tape {
    pos,
    tape,
}

impl Tape {
    fn new() {
        Tape { pos: 0, tape: [0] }
    }

    fn get(self) {
        self.tape[self.pos]
    }

    fn inc(self, x) {
        self.tape[self.pos] = (self.tape[self.pos] + x) % 256;

        if self.tape[self.pos] < 0 {
            self.tape[self.pos] = self.tape[self.pos] + 256;
        }
    }

    fn mov(self, x) {
        self.pos += x;

        while self.pos >= self.tape.len() {
            self.tape.push(0);
        }
    }

    fn set(self, v) {
        self.tape[self.pos] = v;
    }
}

fn run(program, tape, inputs) {
    for op in program {
        match op {
            Op::Inc(x) => tape.inc(x),
            Op::Move(x) => tape.mov(x),
            Op::Loop(program) => while tape.get() != 0 {
                run(program, tape, inputs);
            },
            Op::Print => {
                let c = char::from_i64(tape.get()).expect("A valid char");
                print!("{c}");
            }
            Op::Input => {
                tape.set(0)
            }
        }
    }
}

fn parse(it) {
    let buf = Vec::new();

    while let Some(c) = it.next() {
        let op = match c {
            '+' => Op::Inc(1),
            '-' => Op::Inc(-1),
            '>' => Op::Move(1),
            '<' => Op::Move(-1),
            '.' => Op::Print,
            '[' => Op::Loop(parse(it)),
            ',' => Op::Input,
            ']' => break,
            _ => continue,
        };

        buf.push(op);
    }

    buf
}

struct Program {
    ops,
    inputs,
}

impl Program {
    fn new(code, inputs) {
        Program { ops: parse(code), inputs }
    }

    fn run(self) {
        let tape = Tape::new();
        run(self.ops, tape, self.inputs);
    }
}

fn bf(s, i) {
    let program = Program::new(s.chars(), i);
    program.run();
}

#[bench]
pub fn bf_hello_world(b) {
    b.iter(
        || {
            bf(
                "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.",
                0,
            )
        },
    )
}
