(*---------------------------------------------------------------------------
   Copyright (c) 2021 The cmarkit programmers. All rights reserved.
   SPDX-License-Identifier: ISC
  ---------------------------------------------------------------------------*)

let pp_uchar ppf u = Format.fprintf ppf "U+%04X" (Uchar.to_int u)

let fold_uchars f acc =
  let rec loop f acc u =
    let acc = f acc u in
    if Uchar.equal u Uchar.max then acc else loop f acc (Uchar.succ u)
  in
  loop f acc Uchar.min

let sat_list p =
  let add acc u = if p u then u :: acc else acc in
  List.rev (fold_uchars add [])

(* See https://spec.commonmark.org/current/#unicode-whitespace-character *)
let is_whitespace u =
  let is_zs = Uucp.Gc.general_category u = `Zs in
  let u = Uchar.to_int u in
  is_zs || u = 0x0009 || u = 0x000A || u = 0x000C || u = 0x000d

(* See https://spec.commonmark.org/current/#ascii-punctuation-character *)
let is_ascii_punctuation u =
  let u = Uchar.to_int u in
  (0x0021 <= u && u <= 0x002F) ||
  (0x003A <= u && u <= 0x0040) ||
  (0x005B <= u && u <= 0x0060) ||
  (0x007B <= u && u <= 0x007E)

let is_punctuation u = match Uucp.Gc.general_category u with
| `Pc | `Pd | `Pe | `Pf | `Pi | `Po | `Ps -> true
| _ -> is_ascii_punctuation u

let whitespace_list = sat_list is_whitespace
let punctuation_list = sat_list is_punctuation

let case_fold_map =
  let uchar_map acc u = match Uucp.Case.Fold.fold u with
  | `Self -> acc
  | `Uchars f ->
      let esc u = Printf.sprintf "\\u{%04X}" (Uchar.to_int u) in
      (u, String.concat "" (List.map esc f)) :: acc
  in
  List.rev (fold_uchars uchar_map [])

let byte_size v =
  let words = Obj.reachable_words (Obj.repr v) in
  (words / (Sys.word_size / 8))

let case_fold_count =
  let add acc u = match Uucp.Case.Fold.fold u with
  | `Self -> acc | `Uchars _ -> acc + 1
  in
  fold_uchars add 0

let test () =
  Printf.printf "whitespace: %d characters\n" (List.length whitespace_list);
  Printf.printf "punctuation: %d characters\n" (List.length punctuation_list);
  Printf.printf "non-id case fold: %d characters\n" case_fold_count;
  ()

let year = (Unix.gmtime (Unix.gettimeofday ())).Unix.tm_year + 1900

let gen () =
  let pp_cp ppf u = Format.fprintf ppf "0x%04X" (Uchar.to_int u) in
  let pp_binding ppf (u, s) = Format.fprintf ppf "%a, \"%s\"" pp_cp u s in
  let pp_sep ppf () = Format.fprintf ppf ";@ " in
  let pp_cps ppf us = Format.pp_print_list ~pp_sep pp_cp ppf us in
  let pp_map ppf m = Format.pp_print_list ~pp_sep pp_binding ppf m in
  Format.printf
{|(*---------------------------------------------------------------------------
   Copyright (c) %d The cmarkit programmers. All rights reserved.
   SPDX-License-Identifier: ISC
  ---------------------------------------------------------------------------*)

(* Do not edit. Data generated by support/unicode_data.ml *)

let unicode_version = "%s"

let whitespace =@?
  @[<1>[|%a|]@]

let punctuation =@?
  @[<1>[|%a|]@]

let case_fold =@?
  @[<1>[|%a|]@]
%!|} year Uucp.unicode_version pp_cps whitespace_list pp_cps punctuation_list
     pp_map case_fold_map

let main () = match Array.to_list Sys.argv with
| _ :: "-t" :: [] -> test ()
| _ :: [] -> gen ()
| _ -> Printf.printf "Usage: %s [-t]\n%!" Sys.argv.(0)

let () = if !Sys.interactive then () else main ()
