///|
fn main {
  let args = @env.args()
  if args.length() < 3 ||
    (
      args[1] != "roundtrip" &&
      args[1] != "roundtrip-compatible" &&
      args[1] != "inspect" &&
      args[1] != "generate" &&
      args[1] != "verify" &&
      args[1] != "subgraph" &&
      args[1] != "csr" &&
      args[1] != "csr-incoming"
    ) ||
    (args.length() != 3 && args[1] != "subgraph") {
    println(
      "usage: graphml roundtrip|roundtrip-compatible|inspect|verify|csr|csr-incoming INPUT.graphml OR graphml subgraph INPUT.graphml [NODE_ID ...] OR graphml generate SEED",
    )
    @sys.exit(2)
    return
  }
  if args[1] == "generate" {
    let seed = @string.parse_int(args[2]) catch {
      _ => {
        println("ERROR: seed must be an integer")
        @sys.exit(2)
        return
      }
    }
    let output = generate(seed) catch {
      error => {
        println("ERROR: \{error.message()}")
        @sys.exit(1)
        return
      }
    }
    println(output)
    return
  }
  let input = @fs.read_file_to_string(args[2]) catch {
    error => {
      let @fs.IOError(message) = error
      println("ERROR: \{message}")
      @sys.exit(1)
      return
    }
  }
  let profile = if args[1] == "roundtrip-compatible" {
    @gml.ReferenceLibraries
  } else {
    @gml.Strict
  }
  let document = @gml.parse(input, profile~) catch {
    error => {
      println("ERROR: \{error.message()}")
      @sys.exit(1)
      return
    }
  }
  if args[1] == "subgraph" || args[1] == "csr" || args[1] == "csr-incoming" {
    let output = export_selection(document, args) catch {
      error => {
        println("ERROR: \{error.message()}")
        @sys.exit(1)
        return
      }
    }
    println(output)
    return
  }
  if args[1] == "roundtrip" ||
    args[1] == "roundtrip-compatible" ||
    args[1] == "verify" {
    let output = @gml.serialize(document) catch {
      error => {
        println("ERROR: \{error.message()}")
        @sys.exit(1)
        return
      }
    }
    if args[1] == "verify" {
      let recovered = @gml.parse(output) catch {
        error => {
          println("ERROR: \{error.message()}")
          @sys.exit(1)
          return
        }
      }
      if !document.equivalent(recovered) {
        println("ERROR: model changed during roundtrip")
        @sys.exit(1)
        return
      }
      println("OK: model-equivalent roundtrip")
    } else {
      println(output)
    }
  } else {
    let stats = document.statistics()
    println(
      "graphs=\{stats.graphs} nodes=\{stats.nodes} edges=\{stats.edges} hyperedges=\{stats.hyperedges} ports=\{stats.ports} endpoints=\{stats.endpoints}",
    )
    println(
      "mixed=\{stats.has_mixed_direction} parallel=\{stats.has_parallel_edges} loops=\{stats.has_self_loops} locators=\{stats.has_locators} xml_payloads=\{stats.has_xml_payloads}",
    )
  }
}

///|
fn export_selection(
  document : @gml.Document,
  args : Array[String],
) -> String raise @gml.GraphError {
  if document.graph_count() != 1 {
    raise @gml.Unsupported(
      path="cli.graph",
      feature="select exactly one root with the library interface before exporting",
    )
  }
  let flat = document.flat_graph(@gml.GraphAddress::root(0))
  if args[1] == "subgraph" {
    let selection = []
    for i = 3; i < args.length(); i = i + 1 {
      selection.push(args[i])
    }
    return @gml.serialize(flat.induced_subgraph(selection))
  }
  let csr = flat.to_csr(
    orientation=if args[1] == "csr-incoming" {
      @gml.Incoming
    } else {
      @gml.Outgoing
    },
  )
  let output : Json = {
    "node_ids": @json.to_json(csr.node_ids()),
    "indptr": @json.to_json(csr.indptr()),
    "indices": @json.to_json(csr.indices()),
    "edge_indices": @json.to_json(csr.edge_indices()),
    "reversed": @json.to_json(csr.reversed()),
    "orientation": if args[1] == "csr-incoming" {
      "incoming"
    } else {
      "outgoing"
    },
  }
  output.stringify()
}