type t =
  { env : string -> string option
  ; win32 : bool
  ; home_dir : string
  ; mutable cache_dir : string
  ; mutable config_dir : string
  ; mutable data_dir : string
  ; mutable state_dir : string
  ; mutable runtime_dir : string option
  }

let ( / ) = Filename.concat

type known_folder =
  | InternetCache
  | LocalAppData

external get_known_folder_path
  :  known_folder
  -> string option
  = "dune_xdg__get_known_folder_path"

let make t env_var unix_default win32_folder =
  let default =
    if t.win32
    then (
      match get_known_folder_path win32_folder with
      | None -> ""
      | Some s -> s)
    else unix_default
  in
  match t.env env_var with
  | None -> default
  | Some s when Filename.is_relative s -> default
  | Some s -> s
;;

let cache_dir t =
  let home = t.home_dir in
  make t "XDG_CACHE_HOME" (home / ".cache") InternetCache
;;

let config_dir t =
  let home = t.home_dir in
  make t "XDG_CONFIG_HOME" (home / ".config") LocalAppData
;;

let data_dir t =
  let home = t.home_dir in
  make t "XDG_DATA_HOME" (home / ".local" / "share") LocalAppData
;;

let state_dir t =
  let home = t.home_dir in
  make t "XDG_STATE_HOME" (home / ".local" / "state") LocalAppData
;;

let create ?win32 ~env () =
  let win32 =
    match win32 with
    | None -> Sys.win32
    | Some s -> s
  in
  let home_dir =
    let var = if win32 then "USERPROFILE" else "HOME" in
    match env var with
    | None -> ""
    | Some s -> s
  in
  let t =
    { env
    ; win32
    ; home_dir
    ; cache_dir = ""
    ; config_dir = ""
    ; data_dir = ""
    ; state_dir = ""
    ; runtime_dir = None
    }
  in
  t.cache_dir <- cache_dir t;
  t.config_dir <- config_dir t;
  t.data_dir <- data_dir t;
  t.state_dir <- state_dir t;
  t.runtime_dir <- env "XDG_RUNTIME_DIR";
  t
;;

let home_dir t = t.home_dir
let config_dir t = t.config_dir
let data_dir t = t.data_dir
let cache_dir t = t.cache_dir
let state_dir t = t.state_dir
let runtime_dir t = t.runtime_dir
