(define-library (lib c!)
   (version 1.0)
   (description "
      Experimental libc library interface.")

   (export
      errno strerror
      stat
  14.1 Working Directory
      getcwd chdir
  14.2 Accessing Directories
      opendir readdir closedir
      rewinddir telldir seekdir
      scandir alphasort versionsort
      make-selector make-comparer
      file? folder?
      file-selector folder-selector
  14.6 Deleting Files
      unlink rmdir remove
  14.7 Renaming Files
      rename
  14.8 Creating Directories
      mkdir
  14.9 File Attributes
      chown chmod
      truncate
  14.11 Temporary Files
      mktemp mkstemp
      ;; some olvm syscalls
      uname
      isatty
      getenv)

   (import
      (scheme core)
      (otus ffi)
      (scheme bytevector)

      (owl string)
      (owl io)
      (owl math))

(cond-expand
   (Linux
      (begin
         (setq SO (or
            (load-dynamic-library "libc.so")
            (load-dynamic-library "libc.so.6")))

         ; todo: calculate at runtime
         (setq sizeof-dirent (if (eq? (size nullptr) 4) 268 280))
         (setq dirent-d_name (if (eq? (size nullptr) 4)  11  19))
      ))
   (Windows
      (begin
         (setq SO (or                        ; vcrunXXXX
  2003
  2005
  2008
  2010
  2012
  2013
  2015 , 2017 , 2019 , ...
            (runtime-error "No Visual C++ Redistributables are found. Please, install at least one."))) ))
   (Darwin
      (begin
         (setq SO
            (load-dynamic-library "libSystem.dylib"))

         (setq sizeof-dirent 1048)
         (setq dirent-d_name   21)
      ))
   (else
      (begin
         (runtime-error "Unsupported platform" *uname*) )))

; types
(begin
   (setq DIR* type-vptr)
   (setq dirent* type-vptr)
   (setq dirent->string (lambda (dirent)
      (vptr->string (bytevector-copy (vptr->bytevector dirent sizeof-dirent) dirent-d_name))))
)

  14 . File System Interface
(cond-expand
   ((or Linux Darwin)
      (begin
         (setq :strerror (SO type-string "strerror" fft-int))
  Working Directory
         (setq :getcwd (SO type-string "getcwd" type-string fft-int))
  todo : 
         ; Accessing Directories
  todo : fdopendir , dirfd
         (setq :readdir (SO dirent* "readdir" DIR*))
         (setq :closedir (SO fft-int "closedir" DIR*))
         (setq :rewinddir (SO fft-void "rewinddir" DIR*))
         (setq :telldir (SO fft-long "telldir" DIR*))
         (setq :seekdir (SO fft-void "seekdir" DIR* fft-long))
         (setq :scandir (SO fft-int "scandir" type-string (fft& type-vptr) type-callable type-callable))
         ; Deleting Files
         (setq :unlink (SO fft-int "unlink" type-string))
         (setq :rmdir (SO fft-int "rmdir" type-string))
         (setq :remove (SO fft-int "remove" type-string))
         ; Renaming Files
         (setq :rename (SO fft-int "rename" type-string type-string))
         ; 
         (setq :mkdir (SO fft-int "mkdir" type-string fft-unsigned-int))
         (setq :chown (SO fft-int "chown" type-string fft-unsigned-int fft-unsigned-int))
         (setq :fchown (SO fft-int "fchown" type-port fft-unsigned-int fft-unsigned-int))
         (setq :chmod (SO fft-int "chmod" type-string fft-unsigned-int))
         (setq :fchmod (SO fft-int "fchmod" type-port fft-unsigned-int))
         ;; (setq :access (SO fft-int "access" type-string fft-int))
         ;; (setq :truncate (SO fft-int "truncate" type-string fft-signed-long))
         ; Temporary Files
         (setq :mktemp (SO type-string "mktemp" type-string))
         (setq :mkstemp (SO type-port "mkstemp" type-string))


  ( setq :  ( SO ) )
         (setq free (SO fft-void "free" type-vptr))
      ))
   (Windows
      (begin
         (setq :getcwd (SO type-string-wide "_wgetcwd" type-string-wide fft-int))
         (setq :chdir (SO fft-int "_wchdir" type-string-wide))
         (runtime-error
            "Windows platform supported in progress" #n)
      )) )

(begin
   (define (errno) (syscall 60))
   (define (uname) (syscall 63))

   (define strerror :strerror)
   (define stat (case-lambda
      ((filename) (syscall 4 (c-string filename)))
      ((filename follow) (syscall 4 (c-string filename) follow))))

   ; -- libc --------------------------------------
   (define (getcwd)
      (let loop ((n 128))
         (define cwd (:getcwd (make-string n 0) n))
         (if cwd cwd (loop (+ n 64)))))

   (define chdir :chdir)
   (define opendir :opendir)
   (define (readdir dir)
      (define ep (:readdir dir))
      (when ep
         (dirent->string ep)))
   (define closedir :closedir)
   (define rewinddir :rewinddir)
   (define telldir :telldir)
   (define seekdir :seekdir)

   (define alphasort (vm:cast (dlsym (ref SO 2) "alphasort") type-callable))
   (define versionsort (vm:cast (dlsym (ref SO 2) "versionsort") type-callable))

   (define (file? filename)
      (let ((v (stat filename)))
         (when v
            (zero? (band (ref v 3) #o0040000)))))
   (define (folder? filename)
      (let ((v (stat filename)))
         (when v
            (not (zero? (band (ref v 3) #o0040000))))))


   (define (make-selector thunk)
      (make-callback (vm:pin (cons
         (cons fft-int (list
         ;  struct dirent *
            type-vptr))
         (lambda (a)
            (thunk (dirent->string a)))))))

   (define (make-comparer thunk)
      (make-callback (vm:pin (cons
         (cons fft-int (list
         ;  struct dirent **  struct dirent **
            type-vptr         type-vptr))
         (lambda (a b)
            (let*((pa (bytevector->void* (vptr->bytevector a (size nullptr)) 0))
                  (pb (bytevector->void* (vptr->bytevector b (size nullptr)) 0)))
               (thunk (dirent->string pa) (dirent->string pb))))))))

   (define folder-selector (make-selector (lambda (filename)
      (if (folder? filename) 1 0))))

   (define file-selector (make-selector (lambda (filename)
      (if (file? filename) 1 0))))

   (define (scandir filename selector comparer)
      (define ptr (vm:cast 0 type-vptr))
      (define n (:scandir filename ptr selector comparer))
      (define ptrs (vptr->bytevector ptr (* n (size ptr))))
      (let loop ((n n) (out #null))
         (if (eq? n 0)
            out
         else
            (define dirent (bytevector->void* ptrs (* (-- n) (size ptr))))
            (define string (dirent->string dirent))
            (free dirent)
            (loop (-- n) (cons string out)))))

   (define unlink :unlink)
   (define rmdir :rmdir)
   (define remove :remove)
   (define rename :rename)

   (define mkdir (case-lambda
      ((filename) (:mkdir filename #o755)) ; drwxr-xr-x
      ((filename mode) (:mkdir filename mode))))

   (define (chown file owner group)
      (cond
         ((port? file)  (:fchown file owner group))
         ((string? file) (:chown file owner group))))

   (define (chmod file mode)
      (cond
         ((port? file)  (:fchmod file mode))
         ((string? file) (:chmod file mode))))

   (define mktemp :mktemp)
   (define mkstemp :mkstemp)

   ;; (define access :access)

  14.8 Creating Directories

   ; -- olvm --------------------
   (define (isatty fd)
      (syscall 16 fd 19))

   (define (getenv str)
      (syscall 1016 str))
))

 some olvm syscalls
 todo: calculate at runtime
 vcrunXXXX
 types
 Accessing Directories
 Deleting Files
 Renaming Files
 
 (setq :access (SO fft-int "access" type-string fft-int))
 (setq :truncate (SO fft-int "truncate" type-string fft-signed-long))
 Temporary Files
 -- libc --------------------------------------
  struct dirent *
  struct dirent **  struct dirent **
 drwxr-xr-x
 (define access :access)
 -- olvm --------------------
(define-library (lib c!)
   (version 1.0)
   (description "
      Experimental libc library interface.")

   (export
      errno strerror
      stat
  14.1 Working Directory
      getcwd chdir
  14.2 Accessing Directories
      opendir readdir closedir
      rewinddir telldir seekdir
      scandir alphasort versionsort
      make-selector make-comparer
      file? folder?
      file-selector folder-selector
  14.6 Deleting Files
      unlink rmdir remove
  14.7 Renaming Files
      rename
  14.8 Creating Directories
      mkdir
  14.9 File Attributes
      chown chmod
      truncate
  14.11 Temporary Files
      mktemp mkstemp
      uname
      isatty
      getenv)

   (import
      (scheme core)
      (otus ffi)
      (scheme bytevector)

      (owl string)
      (owl io)
      (owl math))

(cond-expand
   (Linux
      (begin
         (setq SO (or
            (load-dynamic-library "libc.so")
            (load-dynamic-library "libc.so.6")))

         (setq sizeof-dirent (if (eq? (size nullptr) 4) 268 280))
         (setq dirent-d_name (if (eq? (size nullptr) 4)  11  19))
      ))
   (Windows
      (begin
  2003
  2005
  2008
  2010
  2012
  2013
  2015 , 2017 , 2019 , ...
            (runtime-error "No Visual C++ Redistributables are found. Please, install at least one."))) ))
   (Darwin
      (begin
         (setq SO
            (load-dynamic-library "libSystem.dylib"))

         (setq sizeof-dirent 1048)
         (setq dirent-d_name   21)
      ))
   (else
      (begin
         (runtime-error "Unsupported platform" *uname*) )))

(begin
   (setq DIR* type-vptr)
   (setq dirent* type-vptr)
   (setq dirent->string (lambda (dirent)
      (vptr->string (bytevector-copy (vptr->bytevector dirent sizeof-dirent) dirent-d_name))))
)

  14 . File System Interface
(cond-expand
   ((or Linux Darwin)
      (begin
         (setq :strerror (SO type-string "strerror" fft-int))
  Working Directory
         (setq :getcwd (SO type-string "getcwd" type-string fft-int))
  todo : 
  todo : fdopendir , dirfd
         (setq :readdir (SO dirent* "readdir" DIR*))
         (setq :closedir (SO fft-int "closedir" DIR*))
         (setq :rewinddir (SO fft-void "rewinddir" DIR*))
         (setq :telldir (SO fft-long "telldir" DIR*))
         (setq :seekdir (SO fft-void "seekdir" DIR* fft-long))
         (setq :scandir (SO fft-int "scandir" type-string (fft& type-vptr) type-callable type-callable))
         (setq :unlink (SO fft-int "unlink" type-string))
         (setq :rmdir (SO fft-int "rmdir" type-string))
         (setq :remove (SO fft-int "remove" type-string))
         (setq :rename (SO fft-int "rename" type-string type-string))
         (setq :mkdir (SO fft-int "mkdir" type-string fft-unsigned-int))
         (setq :chown (SO fft-int "chown" type-string fft-unsigned-int fft-unsigned-int))
         (setq :fchown (SO fft-int "fchown" type-port fft-unsigned-int fft-unsigned-int))
         (setq :chmod (SO fft-int "chmod" type-string fft-unsigned-int))
         (setq :fchmod (SO fft-int "fchmod" type-port fft-unsigned-int))
         (setq :mktemp (SO type-string "mktemp" type-string))
         (setq :mkstemp (SO type-port "mkstemp" type-string))


  ( setq :  ( SO ) )
         (setq free (SO fft-void "free" type-vptr))
      ))
   (Windows
      (begin
         (setq :getcwd (SO type-string-wide "_wgetcwd" type-string-wide fft-int))
         (setq :chdir (SO fft-int "_wchdir" type-string-wide))
         (runtime-error
            "Windows platform supported in progress" #n)
      )) )

(begin
   (define (errno) (syscall 60))
   (define (uname) (syscall 63))

   (define strerror :strerror)
   (define stat (case-lambda
      ((filename) (syscall 4 (c-string filename)))
      ((filename follow) (syscall 4 (c-string filename) follow))))

   (define (getcwd)
      (let loop ((n 128))
         (define cwd (:getcwd (make-string n 0) n))
         (if cwd cwd (loop (+ n 64)))))

   (define chdir :chdir)
   (define opendir :opendir)
   (define (readdir dir)
      (define ep (:readdir dir))
      (when ep
         (dirent->string ep)))
   (define closedir :closedir)
   (define rewinddir :rewinddir)
   (define telldir :telldir)
   (define seekdir :seekdir)

   (define alphasort (vm:cast (dlsym (ref SO 2) "alphasort") type-callable))
   (define versionsort (vm:cast (dlsym (ref SO 2) "versionsort") type-callable))

   (define (file? filename)
      (let ((v (stat filename)))
         (when v
            (zero? (band (ref v 3) #o0040000)))))
   (define (folder? filename)
      (let ((v (stat filename)))
         (when v
            (not (zero? (band (ref v 3) #o0040000))))))


   (define (make-selector thunk)
      (make-callback (vm:pin (cons
         (cons fft-int (list
            type-vptr))
         (lambda (a)
            (thunk (dirent->string a)))))))

   (define (make-comparer thunk)
      (make-callback (vm:pin (cons
         (cons fft-int (list
            type-vptr         type-vptr))
         (lambda (a b)
            (let*((pa (bytevector->void* (vptr->bytevector a (size nullptr)) 0))
                  (pb (bytevector->void* (vptr->bytevector b (size nullptr)) 0)))
               (thunk (dirent->string pa) (dirent->string pb))))))))

   (define folder-selector (make-selector (lambda (filename)
      (if (folder? filename) 1 0))))

   (define file-selector (make-selector (lambda (filename)
      (if (file? filename) 1 0))))

   (define (scandir filename selector comparer)
      (define ptr (vm:cast 0 type-vptr))
      (define n (:scandir filename ptr selector comparer))
      (define ptrs (vptr->bytevector ptr (* n (size ptr))))
      (let loop ((n n) (out #null))
         (if (eq? n 0)
            out
         else
            (define dirent (bytevector->void* ptrs (* (-- n) (size ptr))))
            (define string (dirent->string dirent))
            (free dirent)
            (loop (-- n) (cons string out)))))

   (define unlink :unlink)
   (define rmdir :rmdir)
   (define remove :remove)
   (define rename :rename)

   (define mkdir (case-lambda
      ((filename mode) (:mkdir filename mode))))

   (define (chown file owner group)
      (cond
         ((port? file)  (:fchown file owner group))
         ((string? file) (:chown file owner group))))

   (define (chmod file mode)
      (cond
         ((port? file)  (:fchmod file mode))
         ((string? file) (:chmod file mode))))

   (define mktemp :mktemp)
   (define mkstemp :mkstemp)


  14.8 Creating Directories

   (define (isatty fd)
      (syscall 16 fd 19))

   (define (getenv str)
      (syscall 1016 str))
))
