mod buffer;
mod enums;
pub use self::buffer::Buffer;
pub use self::enums::{parse_address, parse_command, Address, Command};

use nom::bytes::complete::take_while;
use nom::character::complete::one_of;
use nom::error::ErrorKind;
use nom::sequence::Tuple;
use nom::Err::Error;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::io::BufReader;

const FILE: &str = "test";

#[derive(PartialEq)]
enum Exit {
    Continue,
    HasBeenModified,
    Quit,
}

// TODO juste $ pour l'instant dans l'adresse
// TODO faire les commandes mais juste p pour l'instant
// TODO faire un système de retour d'erreur pour afficher ? et l'erreur si l'utilisateur le demande
// (cad les fonctions pour parser retournent un Err et on peut parser ce Err pour voir le type de
// l'erreur, idem pour les fonctions d'exécutiosn ?)

/*
fn read_address(input: &str) -> Result<&str, Address> {

}
*/

/*
 * Le char doit être une commande valide.
 */

fn check_address(c: char) -> bool {
    c.is_ascii_digit() || c == '$' || c == ' ' || c == '.'
}

fn check_address_complete(c: char) -> bool {
    check_address(c) || c == ',' || c == ';'
}

fn read_user_input() -> Vec<String> {
    let mut buffer: Vec<String> = Vec::new();
    let mut buffer_input = String::new();
    while buffer_input != "." {
        buffer_input.clear();
        match io::stdin().read_line(&mut buffer_input) {
            Ok(_) => {
                buffer_input.pop();
                if buffer_input != "." {
                    buffer.push(buffer_input.clone());
                }
            }
            Err(e) => panic!("{}", e),
        }
    }

    buffer
}

fn execute_command(buffer: &mut Buffer, c: Command) -> Exit {
    let mut ret = Exit::Continue;

    match c {
        Command::Append(a) => buffer.append(a, read_user_input()),
        Command::Insert(a) => buffer.insert(a, read_user_input()),
        Command::Delete(a) => buffer.delete(a),
        Command::Print(a) => buffer.print(a, false),
        Command::Number(a) => buffer.print(a, true),
        Command::Quit(q) => {
            if buffer.is_modified() {
                if q {
                    ret = Exit::Quit;
                } else {
                    ret = Exit::HasBeenModified;
                }
            } else {
                ret = Exit::Quit;
            }
        }
        Command::Write(a) => {
            buffer.save(a);
            ret = Exit::Continue;
        }
        _ => (),
    }

    ret
}

fn parse_line(input: &str) -> Result<Command, String> {
    let address = take_while(check_address_complete);
    let command = one_of::<_, _, (&str, ErrorKind)>("aidpnqQw");

    //if let Ok((input, (a, c))) = alt(address, command).parse(input) {
    let tmp = (address, command).parse(input);
    if let Ok((_input, (a, c))) = tmp {
        let addr = parse_address(a);
        parse_command(c, addr)
    } else if let Err(Error((_, _))) = tmp {
        Err(String::from("Erreur de parsing"))
    } else {
        Err(String::from("Grosse erreur"))
    }
}

fn main() {
    let buffer: Vec<Result<String, io::Error>> = if let Ok(f) = File::open(FILE) {
        let buff = BufReader::new(f);
        buff.lines().collect()
    } else {
        panic!("Le fichier n'existe pas.");
    };

    let mut buffer_struct = Buffer::new(FILE, buffer);

    println!("fichier lu !");
    let mut pred = Exit::Continue;

    while pred != Exit::Quit {
        let mut input = String::new();
        match io::stdin().read_line(&mut input) {
            Ok(_) => {
                let result_parse = parse_line(&input);
                if let Ok(c) = result_parse {
                    pred = match execute_command(&mut buffer_struct, c) {
                        Exit::HasBeenModified => {
                            if pred == Exit::HasBeenModified {
                                Exit::Quit
                            } else {
                                Exit::HasBeenModified
                            }
                        }
                        exit => exit,
                    }
                } else if let Err(e) = result_parse {
                    println!("?");
                    println!("{}", e);
                    pred = Exit::Continue;
                }
            }
            Err(e) => panic!("{}", e),
        }
    }
}