///|
fn usage() -> Unit {
  println("MoonSpell " + @lib.version())
  println("Usage:")
  println("  moonspell validate  ")
  println("  moonspell check   ...")
  println("  moonspell suggest    [--limit N]")
  println("  moonspell check-file   ")
  println("  moonspell suggest-file    [--limit N]")
  println("  moonspell stem   ")
  println("  moonspell analyze    [--format text|json]")
  println(
    "  moonspell explain    [--limit N] [--format text|json]",
  )
}

///|
fn read_file(path : String) -> String? {
  Some(@lib.strip_bom(@fs.read_file_to_string(path))) catch {
    _ => None
  }
}

///|
fn read_bytes(path : String) -> Bytes? {
  Some(@fs.read_file_to_bytes(path)) catch {
    _ => None
  }
}

///|
fn parse_limit(args : Array[String]) -> Int {
  for i in 5.. String {
  for i in 5.. (@lib.AffixConfig, @lib.Dictionary)? {
  let aff_encoding = match read_bytes(aff_path) {
    Some(bytes) => @lib.detect_aff_encoding(bytes)
    None => {
      println("Failed to read affix file: " + aff_path)
      return None
    }
  }
  let aff_text = match read_bytes(aff_path) {
    Some(bytes) => @lib.decode_bytes(bytes, aff_encoding)
    None => {
      println("Failed to read affix file: " + aff_path)
      return None
    }
  }
  let config = match @lib.parse_aff(aff_text) {
    Ok(value) => value
    Err(_) => {
      println("Failed to parse affix file: " + aff_path)
      return None
    }
  }
  let dictionary = match read_bytes(dic_path) {
    Some(bytes) => {
      let dic_text = @lib.decode_bytes(bytes, aff_encoding)
      match @lib.parse_dic(dic_text) {
        Ok(value) => value
        Err(_) => {
          println("Failed to parse dictionary file: " + dic_path)
          return None
        }
      }
    }
    None => {
      println("Failed to read dictionary file: " + dic_path)
      return None
    }
  }
  Some((config, dictionary))
}

///|
fn validate(args : Array[String]) -> Unit {
  if args.length() < 4 {
    usage()
    return
  }
  match load(args[2], args[3]) {
    Some((config, dictionary)) => {
      println(
        "affix directives parsed: " +
        config.prefixes.length().to_string() +
        " prefixes, " +
        config.suffixes.length().to_string() +
        " suffixes",
      )
      println(
        "dictionary entries parsed: " + dictionary.entries.length().to_string(),
      )
    }
    None => ()
  }
}

///|
fn check(args : Array[String]) -> Unit {
  if args.length() < 5 {
    usage()
    return
  }
  match load(args[2], args[3]) {
    Some((config, dictionary)) =>
      for i in 4.. ()
  }
}

///|
fn suggest(args : Array[String]) -> Unit {
  if args.length() < 5 {
    usage()
    return
  }
  match load(args[2], args[3]) {
    Some((config, dictionary)) => {
      let word = args[4]
      let limit = parse_limit(args)
      let suggestions = @lib.suggest(dictionary, config, word, limit)
      if suggestions.length() == 0 {
        println("No suggestions")
      } else {
        for suggestion in suggestions {
          println(suggestion.word + " " + suggestion.distance.to_string())
        }
      }
    }
    None => ()
  }
}

///|
fn check_file(args : Array[String]) -> Unit {
  if args.length() < 5 {
    usage()
    return
  }
  match load(args[2], args[3]) {
    Some((config, dictionary)) => {
      let text = match read_file(args[4]) {
        Some(value) => value
        None => {
          println("Failed to read word file: " + args[4])
          return
        }
      }
      for line in text.split("\n") {
        let word = line.trim().to_owned()
        if !word.is_empty() {
          let status = if @lib.spell(dictionary, config, word).accepted {
            "accepted"
          } else {
            "rejected"
          }
          println(word + "\t" + status)
        }
      }
    }
    None => ()
  }
}

///|
fn suggest_file(args : Array[String]) -> Unit {
  if args.length() < 5 {
    usage()
    return
  }
  match load(args[2], args[3]) {
    Some((config, dictionary)) => {
      let text = match read_file(args[4]) {
        Some(value) => value
        None => {
          println("Failed to read word file: " + args[4])
          return
        }
      }
      let limit = parse_limit(args)
      for line in text.split("\n") {
        let word = line.trim().to_owned()
        if !word.is_empty() {
          let suggestions = @lib.suggest(dictionary, config, word, limit)
          let words : Array[String] = []
          for suggestion in suggestions {
            words.push(suggestion.word)
          }
          println(word + "\t" + words.join(", "))
        }
      }
    }
    None => ()
  }
}

///|
fn stem_cmd(args : Array[String]) -> Unit {
  if args.length() < 5 {
    usage()
    return
  }
  match load(args[2], args[3]) {
    Some((config, dictionary)) =>
      match @lib.stem(dictionary, config, args[4]) {
        Some(value) => println(value)
        None => println("No stem")
      }
    None => ()
  }
}

///|
fn analyze_cmd(args : Array[String]) -> Unit {
  if args.length() < 5 {
    usage()
    return
  }
  match load(args[2], args[3]) {
    Some((config, dictionary)) => {
      let items = @lib.analyze(dictionary, config, args[4])
      if parse_format(args) == "json" {
        println(@lib.analyses_to_json(items))
      } else if items.length() == 0 {
        println("No analysis")
      } else {
        for item in items {
          let stem = match item.stem {
            Some(value) => value
            None => "-"
          }
          println(stem + "\tflags=" + item.flags + "\tmorph=" + item.morphology)
        }
      }
    }
    None => ()
  }
}

///|
fn explain_cmd(args : Array[String]) -> Unit {
  if args.length() < 5 {
    usage()
    return
  }
  match load(args[2], args[3]) {
    Some((config, dictionary)) => {
      let limit = parse_limit(args)
      let item = @lib.explain(dictionary, config, args[4], limit)
      if parse_format(args) == "json" {
        println(@lib.explanation_to_json(item))
      } else {
        for step in item.steps {
          println(step)
        }
        for suggestion in item.suggestions {
          println("suggest: " + suggestion.word)
        }
      }
    }
    None => ()
  }
}

///|
fn main {
  let args = @env.args()
  if args.length() < 2 {
    usage()
    return
  }
  match args[1] {
    "validate" => validate(args)
    "check" => check(args)
    "suggest" => suggest(args)
    "check-file" => check_file(args)
    "suggest-file" => suggest_file(args)
    "stem" => stem_cmd(args)
    "analyze" => analyze_cmd(args)
    "explain" => explain_cmd(args)
    "version" | "--version" | "-v" => println("MoonSpell " + @lib.version())
    _ => usage()
  }
}