extern crate levtree;

use levtree::{CaseInsensitiveKeyChecker, CaseSensitiveKeyChecker};
use levtree::{
    DamerauLevenshteinDistanceCalculator, DistanceCalculator, KeyChecker, LcsDistanceCalculator,
    LevTrie, LevenshteinDistanceCalculator, LevenshteinNoSubDistanceCalculator,
};

use std::io::BufRead;
use std::io::BufReader;

use clap::{Parser, ValueEnum};

#[derive(Debug, Clone, ValueEnum)]
enum Algorithm {
    Lcs,
    Levenshtein,
    LevenshteinNoSub,
    DamerauLevenshtein,
}

/// Encrypt/decrypt files using catenaccio cipher
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct CliArgs {
    /// Word to look up
    #[arg(short, long)]
    wordkey: String,

    /// Number of results returned
    #[arg(short, long, default_value_t = 10)]
    result_size: usize,

    //Specify distance algorithm
    #[arg(short, long, required = false, default_value = "damerau-levenshtein")]
    distance: Algorithm,

    //Toggle case sensitivity
    #[arg(short, long, required = false, default_value_t = false)]
    case_sensitive: bool,
}

impl CliArgs {}

fn run<KC: KeyChecker<char>, DC: DistanceCalculator<char, KC>>(args: &CliArgs) {
    let mut trie = LevTrie::<char, KC>::new();
    let bytes = include_bytes!("words.txt");
    let reader = BufReader::new(&bytes[..]);
    reader
        .lines()
        .map(|line| line.unwrap())
        .for_each(|word: String| {
            trie.add(word.chars());
        });
    let word = args.wordkey.chars().collect::<Vec<char>>();
    let results = trie.fuzzy_search::<DC>(&word, args.result_size);
    for result in results {
        let word: String = trie.lineal_descendant(result.word).into_iter().collect();
        println!("distance: {}, wordkey: {}", result.distance, word);
    }
}

fn main() {
    let args = CliArgs::parse();
    if args.case_sensitive {
        match args.distance {
            Algorithm::Lcs => {
                run::<CaseSensitiveKeyChecker, LcsDistanceCalculator>(&args);
            }
            Algorithm::Levenshtein => {
                run::<CaseSensitiveKeyChecker, LevenshteinDistanceCalculator>(&args);
            }
            Algorithm::LevenshteinNoSub => {
                run::<CaseSensitiveKeyChecker, LevenshteinNoSubDistanceCalculator>(&args);
            }
            Algorithm::DamerauLevenshtein => {
                run::<CaseSensitiveKeyChecker, DamerauLevenshteinDistanceCalculator>(&args);
            }
        }
    } else {
        match args.distance {
            Algorithm::Lcs => {
                run::<CaseInsensitiveKeyChecker, LcsDistanceCalculator>(&args);
            }
            Algorithm::Levenshtein => {
                run::<CaseInsensitiveKeyChecker, LevenshteinDistanceCalculator>(&args);
            }
            Algorithm::LevenshteinNoSub => {
                run::<CaseInsensitiveKeyChecker, LevenshteinNoSubDistanceCalculator>(&args);
            }
            Algorithm::DamerauLevenshtein => {
                run::<CaseInsensitiveKeyChecker, DamerauLevenshteinDistanceCalculator>(&args);
            }
        }
    }
}
