;;;; client-update.lisp

(in-package #:quicklisp-client)

(defun fetch-client-file-info (client-file-info output-file)
  (maybe-fetch-gzipped (file-url client-file-info) output-file)
  (check-client-file output-file client-file-info)
  (probe-file output-file))

(defun retirement-directory (base)
  (let ((suffix 0))
    (loop
      (incf suffix)
      (let* ((try (format nil "~A-~D" base suffix))
             (dir (qmerge (make-pathname :directory
                                         (list :relative "retired" try)))))
        (unless (probe-directory dir)
          (return dir))))))

(defun retire (directory base)
  (let ((retirement-home (qmerge "retired/"))
        (from (truename directory)))
    (ensure-directories-exist retirement-home)
    (let* ((*default-pathname-defaults* retirement-home)
           (to (retirement-directory base)))
      (rename-directory from to)
      to)))

(defun client-update-scratch-directory (client-info)
  (qmerge (make-pathname :directory
                         (list :relative
                               "tmp"
                               "client-update"
                               (version client-info)))))

(defun %install-client (new-info local-info)
  (let* ((work-directory (client-update-scratch-directory new-info))
         (current-quicklisp-directory (qmerge "quicklisp/"))
         (new-quicklisp-directory
          (merge-pathnames "quicklisp/" work-directory))
         (local-temp-tar (merge-pathnames "quicklisp.tar" work-directory))
         (local-setup (merge-pathnames "setup.lisp" work-directory))
         (local-asdf (merge-pathnames "asdf.lisp" work-directory))
         (new-client-tar-p (not (info-equal (client-tar-info new-info)
                                            (client-tar-info local-info))))
         (new-setup-p (not (info-equal (setup-info new-info)
                                       (setup-info local-info))))
         (new-asdf-p (not (info-equal (asdf-info new-info)
                                      (asdf-info local-info)))))
    (ensure-directories-exist work-directory)
    ;; Fetch and unpack quicklisp.tar if needed
    (when new-client-tar-p
      (fetch-client-file-info (client-tar-info new-info) local-temp-tar)
      (unpack-tarball local-temp-tar :directory work-directory))
    ;; Fetch setup.lisp if needed
    (when new-setup-p
      (fetch-client-file-info (setup-info new-info) local-setup))
    ;; Fetch asdf.lisp if needed
    (when new-asdf-p
      (fetch-client-file-info (asdf-info new-info) local-asdf))
    ;; Everything fetched, so move the old stuff away and move the new
    ;; stuff in
    (when new-client-tar-p
      (retire (qmerge "quicklisp/")
              (format nil "quicklisp-~A"
                      (version local-info)))
      (rename-directory new-quicklisp-directory current-quicklisp-directory))
    (when new-setup-p
      (replace-file local-setup (qmerge "setup.lisp")))
    (when new-asdf-p
      (replace-file local-asdf (qmerge "asdf.lisp")))
    ;; But unconditionally move the new client-info into place
    (replace-file (source-file new-info) (qmerge "client-info.sexp"))
    new-info))

(defun update-client (&key (prompt t))
  (let* ((local-info (local-client-info))
         (newest-info (newest-client-info local-info)))
    (cond ((null newest-info)
           (format t "No client update available.~%"))
          ((client-version-lessp local-info newest-info)
           (format t "Updating client from version ~A to version ~A.~%"
                   (version local-info)
                   (version newest-info))
           (when (or (not prompt)
                     (press-enter-to-continue))
             (%install-client newest-info local-info)
             (format t "~&New Quicklisp client installed. ~
                          It will take effect on restart.~%")))
          (t
           (format t "The most up-to-date client, version ~A, ~
                      is already installed.~%"
                   (version local-info)))))
  t)


(defun install-client (&key url version)
  (unless (or url version)
    (error "One of ~S or ~S is required" :url :version))
  (when (and url version)
    (error "Only one of ~S or ~S is allowed" :url :version))
  (when version
    (setf url (client-info-url-from-version version)))
  (let ((local-info (local-client-info))
        (new-info (fetch-client-info url)))
    (%install-client new-info local-info)))
