///|
const FileEntryCode =
  #|pub enum FileEntry {
  #|  Dir(Map[String, FileEntry])
  #|  File(Bytes)
  #|} derive(Show)

///|
fn main {
  let dir = @ref.new("assets")
  let output = @ref.new("assets.mbt")
  let var_name = @ref.new("assets")
  let is_pub = @ref.new(false)
  let include_entry = @ref.new(false)
  let argv = @sys.get_cli_args()
  let usage = " usage: \{argv[0]} [options]"
  @ArgParser.parse(
    [
      ("--dir", "-d", Set_string(dir), "assets directory"),
      ("--output", "-o", Set_string(output), "output directory"),
      ("--var", "-v", Set_string(var_name), "variable name"),
      ("--pub", "-p", Set(is_pub), "make the variable public"),
      ("--include-entry", "-i", Set(include_entry), "include the entry point"),
    ],
    ignore,
    usage,
    argv,
  )
  if dir.val == "" {
    println("Error: directory is not specified")
    return
  }
  let assets = scan_dir(dir.val)
  let code = (if include_entry.val { "\{FileEntryCode}\n" } else { "" }) +
    (if is_pub.val { "pub " } else { "" }) +
    "let \{var_name.val}: FileEntry = \{assets.to_string()}"
  if (try? @fs.write_string_to_file(output.val, code)) is Err(e) {
    println("Error: failed to write output file: \{e}")
    return
  }
}

///|
fn scan_dir(dir : String) -> FileEntry {
  let map = {}
  if (try? @fs.read_dir(dir)) is Ok(list) {
    for file_name in list {
      let file_path = dir + "/" + file_name
      if (try? @fs.is_dir(file_path)) is Ok(true) {
        let submap = scan_dir(file_path)
        map.set(file_name, submap)
      } else if (try? @fs.read_file_to_bytes(file_path)) is Ok(data) {
        map.set(file_name, File(data))
      }
    }
  }
  Dir(map)
}

///|
pub fn FileEntry::extract_dir(
  self : FileEntry,
  prefix? : String,
) -> Unit raise Failure {
  if self is Dir(map) {
    for file_name, value in map {
      if value is Dir(_) {
        value.extract_dir(prefix?=prefix.map(p => p + "/" + file_name))
      } else if value is File(bytes) {
        let file_path = prefix
          .map(p => p + "/" + file_name)
          .unwrap_or(file_name)
        if (try? @fs.write_bytes_to_file(file_path, bytes)) is Err(e) {
          raise Failure("failed to write file: \{e}")
        }
      }
    }
  } else {
    raise Failure("not a directory")
  }
}