Wiki Home
open System
open System.IO


type Router =
  {
    nickname: string
    fingerprint: string
    ip: string 
    flags: string list
    meta: Map<string, string>
  }

let emptyRouter = { nickname=""; fingerprint=""; ip=""; flags=[]; meta=Map.empty }

let (|RouterData|_|) (s: string) = 
  match s.Split(' ') |> Array.toList with 
  | "r"::rest -> Some { nickname=rest[0]; fingerprint=rest[1]; ip=rest[5]; flags=[]; meta=Map.empty}
  | _ -> None

let (|Flags|_|) (s: string) = 
  match s.Split(' ') |> Array.toList with 
  | "s"::rest -> Some rest
  | _ -> None

let (|Meta|_|) (s: string) = 
  match s.Split(' ') |> Array.toList with 
  | "w"::rest -> 
    Some (rest 
      |> List.map (fun kv -> kv.Split("=")) 
      |> List.map (fun kv -> kv[0], kv[1])
      |> Map.ofList )
  | _ -> None


let parse (lines: string list) : Router list =
  let rec parseDetails (lines: string list) (router: Router): Router =
    match lines with
    | (Flags f)::t -> parseDetails t { router with flags = f }
    | (Meta m)::t -> parseDetails t { router with meta = m }
    | _ -> router

  let rec parseRouters (lines: string list) (routers: Router list): Router list =
    match lines with
    | (RouterData r)::t -> parseRouters t ((parseDetails t r)::routers)
    | (h::t) -> parseRouters t routers 
    | [] -> routers 

  parseRouters lines []

let contains l2 l1 =
  Set.ofList l1 |> Set.isSubset (Set.ofList ["Exit"; "Fast"; "Stable"; "Running"; "Stable"; "Valid"])

let isCandidate flags = 
  flags |> contains ["Exit"; "Fast"; "Stable"; "Running"; "Stable"; "Valid"]

let isBadCandidate flags =
  flags |> contains ["BadExit"]

let toRow r =
  $"| {r.nickname} | {r.fingerprint} | {r.ip} | {String.Join(',', r.flags)} | {String.Join(',', r.meta)} |"


fun _ -> Console.ReadLine()
|> Seq.initInfinite
|> Seq.takeWhile ((<>) null)
|> Seq.toList
|> parse
|> Seq.filter (fun r -> isCandidate r.flags)
|> Seq.filter (fun r -> r.flags |> List.contains "BadExit" |> not )
|> Seq.sortByDescending (fun x -> x.meta |> Map.tryFind "Bandwidth" |> Option.defaultValue "0" |> int ) 
|> Seq.map toRow
|> Seq.iter (Console.WriteLine)