(* Copyright © 2026 Stéphane Glondu. Licensed under GPL-2+. *)

open Js_of_ocaml

module Qjs = struct
  open Js

  class type file = object
    method close : unit meth
    method puts : js_string t -> unit meth
    method flush : unit meth
    method readAsString : js_string t meth
  end

  class type std = object
    method exit : int -> unit meth
    method out : file t readonly_prop
    method loadFile : js_string t -> js_string t opt meth
    method open_ : js_string t -> js_string t -> file t opt meth
    method popen : js_string t -> js_string t -> file t opt meth
  end

  class type os = object
    method exec : js_string t js_array t -> int meth
  end

  class type global = object
    method scriptArgs : js_string t js_array t readonly_prop
    method std : std t readonly_prop
    method os : os t readonly_prop
  end

  let global : global t = Unsafe.global
end

let getArgs () =
  Qjs.global##.scriptArgs |> Js.to_array |> Array.map Js.to_string
  |> Array.to_list

let exit n =
  Qjs.global##.std##exit n;
  failwith "unreachable"

let loadFile f =
  Js.Opt.case
    (Qjs.global##.std##loadFile (Js.string f))
    (fun () -> None)
    (fun x -> Some (Js.to_string x))

let saveFile f x =
  Js.Opt.case
    (Qjs.global##.std##open_ (Js.string f) (Js.string "w"))
    (fun () -> ())
    (fun f ->
      f##puts (Js.string x);
      f##flush;
      f##close)

let pread x =
  Js.Opt.case
    (Qjs.global##.std##popen (Js.string x) (Js.string "r"))
    (fun () -> None)
    (fun x ->
      let r = Js.to_string x##readAsString in
      x##close;
      Some r)

let printf fmt =
  Printf.ksprintf (fun x -> Qjs.global##.std##.out##puts (Js.string x)) fmt

let exec args =
  Qjs.global##.os##exec
    (args |> Array.of_list |> Array.map Js.string |> Js.array)
