(ns ^{:go-imports ["os" "io/ioutil"]
      :doc "Provides a platform-independent interface to operating system functionality."}
  os)

(def ^{:doc "SIGABRT"
       :added "1.0.1"
       :tag Int
       :const true
       :go "0x6"}
  SIGABRT)

(def ^{:doc "SIGALRM"
       :added "1.0.1"
       :tag Int
       :const true
       :go "0xe"}
  SIGALRM)

(def ^{:doc "SIGFPE"
       :added "1.0.1"
       :tag Int
       :const true
       :go "0x8"}
  SIGFPE)

(def ^{:doc "SIGHUP"
       :added "1.0.1"
       :tag Int
       :const true
       :go "0x1"}
  SIGHUP)

(def ^{:doc "SIGILL"
       :added "1.0.1"
       :tag Int
       :const true
       :go "0x4"}
  SIGILL)

(def ^{:doc "SIGINT"
       :added "1.0.1"
       :tag Int
       :const true
       :go "0x2"}
  SIGINT)

(def ^{:doc "SIGKILL"
       :added "1.0.1"
       :tag Int
       :const true
       :go "0x9"}
  SIGKILL)

(def ^{:doc "SIGPIPE"
       :added "1.0.1"
       :tag Int
       :const true
       :go "0xd"}
  SIGPIPE)

(def ^{:doc "SIGQUIT"
       :added "1.0.1"
       :tag Int
       :const true
       :go "0x3"}
  SIGQUIT)

(def ^{:doc "SIGSEGV"
       :added "1.0.1"
       :tag Int
       :const true
       :go "0xb"}
  SIGSEGV)

(def ^{:doc "SIGTERM"
       :added "1.0.1"
       :tag Int
       :const true
       :go "0xf"}
  SIGTERM)

(def ^{:doc "SIGTRAP"
       :added "1.0.1"
       :tag Int
       :const true
       :go "0x5"}
  SIGTRAP)

(defn chmod
  "Changes the mode of the named file to mode. If the file is a symbolic link, it changes the mode of the link's target."
  {:added "1.0"
   :go "! err := os.Chmod(name, os.FileMode(mode)); PanicOnErr(err); _res := NIL"}
  [^String name, ^Int mode])

(defn chown
  "Changes the numeric uid and gid of the named file. If the file is a symbolic link,
  it changes the uid and gid of the link's target. A uid or gid of -1 means to not change that value."
  {:added "1.0"
   :go "! err := os.Chown(name, uid, gid); PanicOnErr(err); _res := NIL"}
  [^String name, ^Int uid, ^Int gid])

(defn chtimes
  "Changes the access and modification times of the named file, similar to the Unix utime() or utimes() functions."
  {:added "1.0"
   :go "! err := os.Chtimes(name, atime, mtime); PanicOnErr(err); _res := NIL"}
  [^String name, ^Time atime, ^Time mtime])

(defn clearenv
  "Deletes all environment variables."
  {:added "1.0"
   :go "! os.Clearenv(); _res := NIL"}
  [])

(defn env
  "Returns a map representing the environment."
  {:added "1.0"
   :go "env()"}
  [])

(defn ^String executable
  "Returns the path name for the executable that started the current process."
  {:added "1.0"
   :go "! _res, err := os.Executable(); PanicOnErr(err)"}
  [])

(defn ^String expand-env
  "Replaces ${var} or $var in the string according to the values of the current environment variables.
  References to undefined variables are replaced by the empty string."
  {:added "1.0"
   :go "os.ExpandEnv(s)"}
  [^String s])

(defn ^Int egid
  "Returns the numeric effective group id of the caller."
  {:added "1.0"
   :go "os.Getegid()"}
  [])

(defn ^Int gid
  "Returns the numeric group id of the caller."
  {:added "1.0"
   :go "os.Getgid()"}
  [])

(defn ^Int euid
  "Returns the numeric effective user id of the caller."
  {:added "1.0"
   :go "os.Geteuid()"}
  [])

(defn ^Int uid
  "Returns the numeric user id of the caller."
  {:added "1.0"
   :go "os.Getuid()"}
  [])

(defn ^Int pid
  "Returns the process id of the caller."
  {:added "1.0"
   :go "os.Getpid()"}
  [])

(defn ^Int ppid
  "Returns the process id of the caller's parent."
  {:added "1.0"
   :go "os.Getppid()"}
  [])

(defn ^{:tag [Int]} groups
  "Returns a list of the numeric ids of groups that the caller belongs to."
  {:added "1.0"
   :go "! _res, err := os.Getgroups(); PanicOnErr(err)"}
  [])

(defn ^Int pagesize
  "Returns the underlying system's memory page size."
  {:added "1.0"
   :go "os.Getpagesize()"}
  [])

(defn ^Boolean path-separator?
  "Reports whether c is a directory separator character."
  {:added "1.0"
   :go "os.IsPathSeparator(uint8(c))"}
  [^Char c])

(defn lchown
  "Changes the numeric uid and gid of the named file. If the file is a symbolic link,
  it changes the uid and gid of the link itself."
  {:added "1.0"
   :go "! err := os.Lchown(name, uid, gid); PanicOnErr(err); _res := NIL"}
  [^String name, ^Int uid, ^Int gid])

(defn link
  "Creates newname as a hard link to the oldname file."
  {:added "1.0"
   :go "! err := os.Link(oldname, newname); PanicOnErr(err); _res := NIL"}
  [^String oldname, ^String newname])

(defn ^String hostname
  "Returns the host name reported by the kernel."
  {:added "1.0"
   :go "! _res, err := os.Hostname(); PanicOnErr(err)"}
  [])

(defn set-env
  "Sets the value of the environment variable named by the key."
  {:added "1.0"
   :go "! err := os.Setenv(key, value); PanicOnErr(err); _res := NIL"}
  [^String key ^String value])

(defn unset-env
  "Unsets a single environment variable."
  {:added "1.0"
   :go "! err := os.Unsetenv(key); PanicOnErr(err); _res := NIL"}
  [^String key])

(defn get-env
  "Returns the value of the environment variable named by the key or nil if the variable is not present in the environment."
  {:added "1.0"
   :go "getEnv(key)"}
  [^String key])

(defn args
  "Returns a sequence of the command line arguments, starting with the program name (normally, joker)."
  {:added "1.0"
   :go "commandArgs()"}
  [])

(defn exit
  "Causes the current program to exit with the given status code (defaults to 0)."
  {:added "1.0"
   :go {1 "NIL; ExitJoker(code)"
        0 "NIL; ExitJoker(0)"}}
  ([^Int code])
  ([]))

(defn sh
  "Executes the named program with the given arguments. Returns a map with the following keys:
      :success - whether or not the execution was successful,
      :err-msg (present iff :success if false) - string capturing error object returned by Go runtime
      :exit - exit code of program (or attempt to execute it),
      :out - string capturing stdout of the program,
      :err - string capturing stderr of the program."
  {:added "1.0"
   :go "sh(\"\", nil, nil, nil, name, arguments)"}
  [^String name & ^String arguments])

(defn sh-from
  "Executes the named program with the given arguments and working directory set to dir.
  Returns a map with the following keys:
      :success - whether or not the execution was successful,
      :err-msg (present iff :success if false) - string capturing error object returned by Go runtime
      :exit - exit code of program (or attempt to execute it),
      :out - string capturing stdout of the program,
      :err - string capturing stderr of the program."
  {:added "1.0"
   :go "sh(dir, nil, nil, nil, name, arguments)"}
  [^String dir ^String name & ^String arguments])

(defn exec
  "Executes the named program with the given arguments. opts is a map with the following keys (all optional):
  :args - vector of arguments (all arguments must be strings),
  :dir - if specified, working directory will be set to this value before executing the program,
  :stdin - if specified, provides stdin for the program. Can be either a string or an IOReader.
  If it's a string, the string's content will serve as stdin for the program. IOReader can be, for example,
  *in* (in which case Joker's stdin will be redirected to the program's stdin) or the value returned by (joker.os/open).
  :stdout - if specified, must be an IOWriter. It can be, for example, *out* (in which case the program's stdout will be redirected
  to Joker's stdout) or the value returned by (joker.os/create).
  :stderr - the same as :stdout, but for stderr.
  Returns a map with the following keys:
  :success - whether or not the execution was successful,
  :err-msg (present iff :success if false) - string capturing error object returned by Go runtime
  :exit - exit code of program (or attempt to execute it),
  :out - string capturing stdout of the program (unless :stdout option was passed)
  :err - string capturing stderr of the program (unless :stderr option was passed)."
  {:added "1.0"
   :go "execute(name, opts)"}
  [^String name ^Map opts])

(defn ^Int start
  "Starts a new process with the program specified by name.
  opts is a map with the same keys as in exec.
  Doesn't wait for the process to finish.
  Returns the process's PID."
  {:added "1.0.1"
   :go "startProcess(name, opts)"}
  [^String name ^Map opts])

(defn kill
  "Causes the process with the given PID to exit immediately.
  Only kills the process itself, not any other processes it may have started."
  {:added "1.0.1"
   :go "killProcess(pid)"}
  [^Int pid])

(defn signal
  "Sends signal to the process with the given PID."
  {:added "1.0.1"
   :go "sendSignal(pid, signal)"}
  [^Int pid ^Int signal])

(defn watch
  "Watches paths for file system changes, sends event maps to ch, and returns a
  zero-argument cancel function. paths must be Seqable and each path must be a
  string. opts may contain :recursive? to watch child directories recursively.

  Event maps have {:type :event :path path :ops ops}, where ops is a set
  containing one or more of :create, :write, :remove, :rename, and :chmod.
  Runtime watcher errors are sent as {:type :error :error error}.

  Calling the returned cancel function stops the watcher and closes ch."
  {:added "1.7.2"
   :go {2 "watch(paths, ch, EmptyArrayMap())"
        3 "watch(paths, ch, opts)"}}
  ([^Seqable paths ^Channel ch])
  ([^Seqable paths ^Channel ch ^Map opts]))

(defn mkdir
  "Creates a new directory with the specified name and permission bits."
  {:added "1.0"
   :go "! err := os.Mkdir(name, os.FileMode(perm)); PanicOnErr(err); _res := NIL"}
  [^String name ^Int perm])

(defn mkdir-all
  "Creates a directory named path, along with any necessary parents, and returns nil, or else throws an error.
  The permission bits perm (before umask) are used for all directories that mkdir-all creates.
  If path is already a directory, mkdir-all does nothing and returns nil."
  {:added "1.0"
   :go "! err := os.MkdirAll(name, os.FileMode(perm)); PanicOnErr(err); _res := NIL"}
  [^String name ^Int perm])

(defn ^String read-link
  "Returns the destination of the named symbolic link."
  {:added "1.0"
   :go "! _res, err := os.Readlink(name); PanicOnErr(err)"}
  [^String name])

(defn symlink
  "Creates newname as a symbolic link to oldname."
  {:added "1.0"
   :go "! err := os.Symlink(oldname, newname); PanicOnErr(err); _res := NIL"}
  [^String oldname, ^String newname])

(defn rename
  "Renames (moves) oldpath to newpath. If newpath already exists and is not a directory, rename replaces it."
  {:added "1.0"
   :go "! err := os.Rename(oldpath, newpath); PanicOnErr(err); _res := NIL"}
  [^String oldpath, ^String newpath])

(defn truncate
  "Changes the size of the named file. If the file is a symbolic link, it changes the size of the link's target."
  {:added "1.0"
   :go "! err := os.Truncate(name, int64(size)); PanicOnErr(err); _res := NIL"}
  [^String name, ^Int size])

(defn ^String user-cache-dir
  "Returns the default root directory to use for user-specific cached data.
  Users should create their own application-specific subdirectory within this one and use that.

  On Unix systems, it returns $XDG_CACHE_HOME as specified by https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
  if non-empty, else $HOME/.cache. On Darwin, it returns $HOME/Library/Caches. On Windows, it returns %LocalAppData%.
  On Plan 9, it returns $home/lib/cache.

  If the location cannot be determined (for example, $HOME is not defined), then it will throw an error."
  {:added "1.0"
   :go "! _res, err := os.UserCacheDir(); PanicOnErr(err)"}
  [])

(defn ^String user-config-dir
  "Returns the default root directory to use for user-specific configuration data.
  Users should create their own application-specific subdirectory within this one and use that.

  On Unix systems, it returns $XDG_CONFIG_HOME as specified by https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
  if non-empty, else $HOME/.config. On Darwin, it returns $HOME/Library/Application Support. On Windows, it returns %AppData%.
  On Plan 9, it returns $home/lib.

  If the location cannot be determined (for example, $HOME is not defined), then it will throw an error."
  {:added "1.0"
   :go "! _res, err := os.UserConfigDir(); PanicOnErr(err)"}
  [])

(defn ^String user-home-dir
  "Returns the current user's home directory.

  On Unix, including macOS, it returns the $HOME environment variable. On Windows, it returns %USERPROFILE%.
  On Plan 9, it returns the $home environment variable."
  {:added "1.0"
   :go "! _res, err := os.UserHomeDir(); PanicOnErr(err)"}
  [])

(defn ls
  "Reads the directory named by dirname and returns a list of directory entries sorted by filename.
  Each entry is a map with the following keys:
  :name - name (String)
  :size - size in bytes (Int)
  :mode - mode (Int)
  :dir? - true if the file is a directory (Boolean)
  :modtime - modification time (unix timestamp) (Int)"
  {:added "1.0"
   :go "readDir(dirname)"}
  [^String dirname])

(defn ^String cwd
  "Returns a rooted path name corresponding to the current directory. If the current directory can
  be reached via multiple paths (due to symbolic links), cwd may return any one of them."
  {:added "1.0"
   :go "! _res, err := os.Getwd(); PanicOnErr(err)"}
  [])

(defn chdir
  "Changes the current working directory to the named directory."
  {:added "1.0"
   :go "! err := os.Chdir(dirname); PanicOnErr(err); _res := NIL"}
  [^String dirname])

(defn stat
  "Returns a map describing the named file. The info map has the following attributes:
  :name - base name of the file
  :size - length in bytes for regular files; system-dependent for others
  :mode - file mode bits
  :modtime - modification time
  :dir? - true if file is a directory"
  {:added "1.0"
   :go "! _info, err := os.Stat(filename); PanicOnErr(err); _res := FileInfoMap(_info.Name(), _info)"}
  [^String filename])

(defn lstat
  "Like stat, but if the file is a symbolic link, the result describes the symbolic link."
  {:added "1.0"
   :go "! _info, err := os.Lstat(filename); PanicOnErr(err); _res := FileInfoMap(_info.Name(), _info)"}
  [^String filename])

(defn ^Boolean exists?
  "Returns true if file or directory with the given path exists. Otherwise returns false."
  {:added "1.0"
   :go "exists(path)"}
  [^String path])

(defn ^File open
  "Opens the named file for reading. If successful, the file can be used for reading;
  the associated file descriptor has mode O_RDONLY."
  {:added "1.0"
   :go "! _res, err := os.Open(name); PanicOnErr(err)"}
  [^String name])

(defn ^File create
  "Creates the named file with mode 0666 (before umask), truncating it if it already exists."
  {:added "1.0"
   :go "! _res, err := os.Create(name); PanicOnErr(err)"}
  [^String name])

(defn close
  "Closes the file, rendering it unusable for I/O."
  {:added "1.0"
   :go "! err := f.Close(); PanicOnErr(err); _res := NIL"}
  [^File f])

(defn remove
  "Removes the named file or (empty) directory."
  {:added "1.0"
   :go "! err := os.Remove(name); PanicOnErr(err); _res := NIL"}
  [^String name])

(defn remove-all
  "Removes path and any children it contains.

  It removes everything it can, then panics with the first error (if
  any) it encountered."
  {:added "1.0"
   :go "! err := os.RemoveAll(path); PanicOnErr(err); _res := NIL"}
  [^String path])

(defn ^String temp-dir
  "Returns the default directory to use for temporary files.
  On Unix systems, it returns $TMPDIR if non-empty, else /tmp.
  On Windows, it uses GetTempPath, returning the first non-empty
  value from %TMP%, %TEMP%, %USERPROFILE%, or the Windows directory.
  The directory is neither guaranteed to exist nor have accessible permissions."
  {:added "1.0"
   :go "os.TempDir()"}
  [])

(defn ^String mkdir-temp
  "Creates a new temporary directory in the directory dir.
  The directory name is generated by taking pattern and applying a random string to the end.
  If pattern includes a \"*\", the random string replaces the last \"*\".
  Returns the name of the new directory. If dir is the empty string,
  uses the default directory for temporary files (see joker.os/temp-dir).
  Multiple programs calling joker.os/mkdir-temp simultaneously will not choose the same directory.
  It is the caller's responsibility to remove the directory when no longer needed."
  {:added "1.0"
   :go "! _res, err := ioutil.TempDir(dir, pattern); PanicOnErr(err);"}
  [^String dir ^String pattern])

(defn ^File create-temp
  "Creates a new temporary file in the directory dir, opens the file for reading and writing,
  and returns the resulting File. The filename is generated by taking pattern and adding a
  random string to the end. If pattern includes a \"*\", the random string replaces the last \"*\".
  If dir is the empty string, uses the default directory for temporary files (see joker.os/temp-dir).
  Multiple programs calling joker.os/make-temp-file simultaneously will not choose the same file.
  The caller can use (name f) to find the pathname of the file.
  It is the caller's responsibility to remove the file when no longer needed."
  {:added "1.0"
   :go "! _res, err := ioutil.TempFile(dir, pattern); PanicOnErr(err);"}
  [^String dir ^String pattern])
