open Fugue

exception EmptyFilename
exception InvalidFilename of string

type filepath = {
  absolute : bool;
  filepath : string list;
}

type filename = { filename : string }

let is_absolute fp = fp.absolute
let empty_fn = { filename = "" }
let current_dir = { absolute = false; filepath = [] }

let fp_to_string x =
  match (x.filepath, x.absolute) with
  | [], true -> "/"
  | [], false -> "./"
  | l, true -> "/" ^ String.concat Filename.dir_sep l
  | l, false -> String.concat Filename.dir_sep l

let fn_to_string x = x.filename

(** [got_dirsep s] returns [true] if [s] contains [Filename.dir_sep], i.e. "/" on Unix. *)
let got_dirsep x =
  let gotDirsep = ref false in
  let dirsepLen = String.length Filename.dir_sep in
  for i = 0 to String.length x - dirsepLen - 1 do
    if String.sub x i dirsepLen = Filename.dir_sep then
      gotDirsep := true
  done;
  !gotDirsep

(* this only strip the last / if it exists *)
let fp x =
  (* TODO fix it properly, however separator is always a single char *)
  match String_utils.split Filename.dir_sep.[0] x with
  | "" :: p -> { absolute = true; filepath = List.filter (fun x -> x <> "." && x <> "") p }
  | p -> { absolute = false; filepath = List.filter (fun x -> x <> "." && x <> "") p }

let fn = function
  | "" | "." | ".." -> raise EmptyFilename
  | filename when got_dirsep filename -> raise (InvalidFilename filename)
  | filename -> { filename }

let valid_fn x =
  try
    let _ = fn x in
    true
  with EmptyFilename | InvalidFilename _ -> false

let ( <//> ) (afp : filepath) (bfp : filepath) =
  match (afp.absolute, bfp.absolute) with
  | _, true -> failwith "the second argument cannot be an absolute path"
  | _ -> { absolute = afp.absolute; filepath = afp.filepath @ bfp.filepath }

let ( </> ) (afp : filepath) (bfp : filename) =
  { absolute = afp.absolute; filepath = afp.filepath @ [ bfp.filename ] }

let ( <.> ) (afp : filename) ext = fn (afp.filename ^ "." ^ ext)
let path_length path = List.length path.filepath
let path_dirname path = { path with filepath = list_init path.filepath }
let path_basename path = fn (list_last path.filepath)
let in_current_dir (x : filename) = fp x.filename
let chop_extension (x : filename) = fn (Filename.chop_extension (fn_to_string x))
