use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::process;

struct Bfck {
    quiet: usize,
    dataptr: usize,
    data: [u8; 128],
}

impl Bfck {
    fn new(quiet: usize) -> Self {
        Bfck {
            quiet,
            dataptr: 0,
            data: [0; 128],
        }
    }

    fn interpret(&mut self, code: &mut [u8]) {
        let mut pc = 0;
        while pc < code.len() {
            if self.quiet > 3 && b"<>+-,.[]\n".contains(&code[pc]) {
                eprint!("{}", char::from_u32(code[pc] as u32).unwrap());
            }
            match code[pc] {
                b'<' => self.dataptr -= 1,
                b'>' => self.dataptr += 1,
                b'+' => self.data[self.dataptr] += 1,
                b'-' => self.data[self.dataptr] -= 1,
                b'.' => {
                    print!("{}", char::from_u32(self.data[self.dataptr] as u32).unwrap());
                    io::stdout().flush().unwrap();
                }
                b',' => self.data[self.dataptr] = io::stdin().bytes().next().unwrap().unwrap(),
                b'[' => {
                    let mut bracketct = 1;
                    let sub_pc = pc + 1;
                    while bracketct != 0 && pc < code.len() - 1 {
                        pc += 1;
                        bracketct += if code[pc] == b'[' { 1 } else { 0 };
                        bracketct -= if code[pc] == b']' { 1 } else { 0 };
                    }
                    if bracketct == 0 {
                        while self.data[self.dataptr] != 0 {
                            self.interpret(&mut code[sub_pc..pc]);
                        }
                    }
                }
                b']' => {
                    println!("UNBALANCED BRACKETS");
                    process::exit(-1);
                }
                b'#' => {
                    if self.quiet > 2 {
                        println!("data: {:?} (ptr: {})\n", &self.data[0..10], self.dataptr);
                    }
                }
                _ => (),
            }
            if self.dataptr > 100 {
                println!("RANGE ERROR");
                process::exit(-1);
            }
            pc += 1;
        }
    }
}

fn main() {
    let args: Vec<_> = std::env::args().collect();
    let mut file = File::open(&args[1]).unwrap();
    let mut code = Vec::new();
    file.read_to_end(&mut code).unwrap();

    let mut bfck = Bfck::new(args.len());
    bfck.interpret(&mut code);
}