///|
fn paste_command() -> @argparse.Command {
  Command(
    "paste",
    about="Merge corresponding lines of each input.",
    flags=[
      FlagArg(
        "serial",
        short='s',
        about="Paste one file at a time instead of in parallel.",
      ),
    ],
    options=[
      OptionArg(
        "delimiters",
        short='d',
        about="Reuse characters from LIST instead of TAB (\\t, \\n, \\0, \\\\ supported).",
      ),
    ],
    positionals=[
      PositionArg(
        "files",
        about="[file...] ('-' or no file reads stdin)",
        num_args=ValueRange(lower=0),
      ),
    ],
    disable_help_subcommand=true,
  )
}

///|
fn option_value(matches : @argparse.Matches, name : String) -> String? {
  match matches.values.get(name) {
    Some(vals) =>
      if vals.is_empty() {
        None
      } else {
        Some(vals[vals.length() - 1])
      }
    None => None
  }
}

///|
fn parse_delimiters(spec : String) -> Array[String] {
  let delims : Array[String] = []
  let chars : Array[Char] = spec.iter().collect()
  let mut i = 0
  while i < chars.length() {
    if chars[i] == '\\' && i + 1 < chars.length() {
      let out = match chars[i + 1] {
        'n' => "\n"
        't' => "\t"
        '0' => ""
        '\\' => "\\"
        c => c.to_string()
      }
      delims.push(out)
      i += 2
    } else {
      delims.push(chars[i].to_string())
      i += 1
    }
  }
  if delims.is_empty() {
    delims.push("\t")
  }
  delims
}

///|
fn split_lines(text : String) -> Array[String] {
  if text is "" {
    return []
  }
  let lines : Array[String] = text.split("\n").map(v => v.to_owned()).collect()
  if text.has_suffix("\n") {
    ignore(lines.pop())
  }
  lines
}

///|
async fn read_source_text(path : String) -> String {
  if path == "-" {
    @stdio.stdin.read_all().text()
  } else {
    @fs.read_file_to_string(path)
  }
}

///|
async fn main {
  let args = @env.args()[1:]
  let command = paste_command()
  let matches = command.parse(argv=args, env=Map([])) catch {
    err => {
      @stdio.stderr.write("\{err}\n")
      @sys.exit(2)
      return
    }
  }
  let serial = matches.flags.get_or_default("serial", false)
  let delims = match option_value(matches, "delimiters") {
    Some(spec) => parse_delimiters(spec)
    None => ["\t"]
  }
  let files = matches.values.get("files").unwrap_or([])
  let sources = if files.is_empty() { ["-"] } else { files }
  let contents : Array[Array[String]] = []
  for path in sources {
    let text = read_source_text(path) catch {
      err => {
        @stdio.stderr.write("paste: \{err}\n")
        @sys.exit(1)
        return
      }
    }
    contents.push(split_lines(text))
  }
  if serial {
    for lines in contents {
      let sb = StringBuilder()
      for index, line in lines {
        if index > 0 {
          sb.write_string(delims[(index - 1) % delims.length()])
        }
        sb.write_string(line)
      }
      sb.write_string("\n")
      @stdio.stdout.write(sb.to_string())
    }
  } else {
    let mut rows = 0
    for lines in contents {
      if lines.length() > rows {
        rows = lines.length()
      }
    }
    for row in 0.. 0 {
          sb.write_string(delims[(index - 1) % delims.length()])
        }
        if row < lines.length() {
          sb.write_string(lines[row])
        }
      }
      sb.write_string("\n")
      @stdio.stdout.write(sb.to_string())
    }
  }
}