use std::env;
use std::io;
use std::fs::File;
//use std::io::BufReader;
use std::io::prelude::*;
use std::io::{Error, ErrorKind};
use std::str::Chars;
use std::mem;
use std::iter::Peekable;
macro_rules! void {
    ( $x:expr) => {{
        let _ = $x;
    }}
}
#[derive(Debug)]
enum TokenType {
    // single-character tokens
    LeftParen, RightParen, LeftBrace, RightBrace,
    Comma, Dot, Minus, Plus, Semicolon, Slash, Star, Colon,
    // one or two character tokens
    Bang, BangEqual,
    Equal, EqualEqual,
    Greater, GreaterEqual,
    Less, LessEqual,
    // literals
    Identifier, Str, Number,
    // keywords
    And, Class, Else, False, Fn, For, If, Nil, Or,
    Print, Return, Super, TSelf, True, Let, While,
    EOF
}
#[derive(Debug)]
enum Literal {
    #[allow(non_camel_case_types)]
    Number(f64),
    #[allow(non_camel_case_types)]
    Str(String),
    #[allow(non_camel_case_types)]
    Bool(bool),
    #[allow(non_camel_case_types)]
    Nil,
}
#[derive(Debug)]
struct Token {
    t_type: TokenType,
    lexeme: String,
    literal: Literal,
    line: usize
}
#[allow(non_snake_case)]
fn Token(t_type: TokenType, lexeme: String, literal: Literal, line: usize) -> Token {
    Token { t_type, lexeme, literal, line }
}
struct Scanner {
    source: String,
    iter: Peekable<Chars<'static>>,
    builder: String,
    tokens: Vec<Token>,
    start: usize,
    current: usize,
    line: usize
}
#[allow(non_snake_case)]
fn Scanner(src: String) -> Scanner {
    let chars = unsafe { mem::transmute(src.chars().peekable()) };
    Scanner { source: src, iter: chars, builder: String::new(), tokens: vec![], start: 0, current: 0, line: 1 }
}
impl Scanner {
    fn scan_tokens(&mut self) -> (&Vec<Token>, bool) {
        let mut has_errs = false;
        while !self.is_end() {
            self.start = self.current;
            let res = self.scan_token();
            has_errs = has_errs || res;
        }
        self.tokens.push(Token(TokenType::EOF, String::new(), Literal::Nil, self.line));
        (&self.tokens, has_errs)
    }
    fn scan_token(&mut self) -> bool {
        let c = self.advance();
        match c {
            Some('(') => { self.builder.push('('); self.add_token(TokenType::LeftParen, Literal::Nil); false },
            Some(')') => { self.builder.push(')'); self.add_token(TokenType::RightParen, Literal::Nil); false },
            Some('{') => { self.builder.push('{'); self.add_token(TokenType::LeftBrace, Literal::Nil); false },
            Some('}') => { self.builder.push('}'); self.add_token(TokenType::RightBrace, Literal::Nil); false },
            Some(',') => { self.builder.push(','); self.add_token(TokenType::Comma, Literal::Nil); false },
            Some('.') => { self.builder.push('.'); self.add_token(TokenType::Dot, Literal::Nil); false },
            Some('-') => { self.builder.push('-'); self.add_token(TokenType::Minus, Literal::Nil); false },
            Some('+') => { self.builder.push('+'); self.add_token(TokenType::Plus, Literal::Nil); false },
            Some(';') => { self.builder.push(';'); self.add_token(TokenType::Semicolon, Literal::Nil); false },
            Some('*') => { self.builder.push('*'); self.add_token(TokenType::Star, Literal::Nil); false },
            Some('!') => { self.builder.push('!'); let matched = self.match_char('='); self.add_token({if matched {TokenType::BangEqual} else {TokenType::Bang}}, Literal::Nil); false },
            Some('=') => { self.builder.push('='); let matched = self.match_char('='); self.add_token({if matched {TokenType::EqualEqual} else {TokenType::Equal}}, Literal::Nil); false },
            Some('>') => { self.builder.push('>'); let matched = self.match_char('='); self.add_token({if matched {TokenType::LessEqual} else {TokenType::Less}}, Literal::Nil); false },
            Some('<') => { self.builder.push('<'); let matched = self.match_char('='); self.add_token({if matched {TokenType::GreaterEqual} else {TokenType::Greater}}, Literal::Nil); false },
            Some('/') => {
                if self.match_char('/') {
                    void!(self.builder.pop());
                    while !self.is_end() && self.iter.peek() != Some(&'\n') {
                        void!(self.advance());
                    }
                }
                else {
                    self.builder.push('/');
                    self.add_token(TokenType::Slash, Literal::Nil);
                }
                false
            },
            Some(' ')  => { false },
            Some('\t') => { false },
            Some('\r') => { false },
            Some('\n') => { self.line += 1; false },
            Some('"')  => { self.string() },
            Some(ch)   => { error(self.line, &format!("Unexpected character: {:?}", ch)) },
            //Some(ch)  => { self.builder.push(ch); }, // ???
            None => { self.current = self.source.len(); false },
        }
    }
    fn advance(&mut self) -> Option<char> {
        self.current += 1;
        self.iter.next()
    }
    fn is_end(&self) -> bool {
        self.current >= self.source.len()
    }
    fn add_token(&mut self, t: TokenType, l: Literal) {
        self.tokens.push(Token(t, self.builder.clone(), l, self.line));
        self.builder = String::new();
    }
    fn match_char(&mut self, ch: char) -> bool {
        if self.is_end() { false }
        else if self.iter.peek() != Some(&ch) { false }
        else {
            self.current += 1;
            let addch = self.iter.next().unwrap();
            self.builder.push(addch);
            true
        }
    }
    fn string(&mut self) -> bool {
        while self.iter.peek() != Some(&'"') && !self.is_end() {
            if self.iter.peek() == Some(&'\n') { self.line += 1; }
            let c = self.advance();
            match c {
                None => { self.current = self.source.len(); },
                Some(ch) => { self.builder.push(ch); },
            }
        }
        if self.is_end() {
            error(self.line, "Unterminated String")
        }
        else {
            void!(self.advance());
            let val = self.builder.clone();
            self.add_token(TokenType::Str, Literal::Str(val));
            false
        }
    }
}
fn report(line: usize, location: &str, msg: &str) -> bool {
    eprintln!("[{}] Error{}: {}", line, location, msg);
    true
}
fn error(line: usize, msg: &str) -> bool {
    report(line, "", msg)
}
fn run(source: String) -> io::Result<bool> {
    let mut had_error = false;
    //for s in source.split(" ").flat_map(|s| s.split("(")).flat_map(|s| s.split(")"))
/*    for s in source.split(" ") {
        print!("{} ", s);
    }*/
    let mut scanner = Scanner(source);
    let tokens = {
        let (temp, tb) = scanner.scan_tokens();
        had_error = tb;
        temp
    };
    // DEBUG
    for token in tokens.iter() {
        println!("{:?}", token);
    }
    Ok(had_error)
}
fn run_file(pfile: &String) -> io::Result<()> {
    let mut file = File::open(pfile)?;
    //let mut buf_reader = BufReader::new(file);
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    match run(contents) {
        Ok(false)  => Ok(()),
        Ok(true) => Err(Error::new(ErrorKind::Other, "Exited with errors")), //TODO replace rust error with custom error type
        Err(err)  => Err(err)
    }
}
macro_rules! reader {
    ($input:expr) => {
        $input.lock().lines()
    }
}
macro_rules! read_line {
    ($iter:expr) => {
        $iter.next().unwrap().unwrap()
    }
}
fn run_prompt() -> io::Result<()> {
    let stdin = io::stdin();
    let mut reader = reader!(stdin);
    loop {
        print!("> ");
        let next_line = read_line!(reader);
        run(next_line)?;
    }
}
fn main() -> io::Result<()> {
    let args: Vec<String> = env::args().collect();
    eprintln!("{:?}", args); //DEBUG
    if args.len() > 2 {
        println!("Usage: rlox [script]");
        Ok(())
    } else if args.len() == 2 {
        run_file(&args[1])
    } else {
        run_prompt()
    }
}
print "Hello world";//aa
+-*{}()!.!===/
//asdasd