; -*- mode: emacs-lisp; lexical-binding: t -*-

(setf eldev-files-to-package `(:or ,eldev-files-to-package '("./bin" "!./bin/**/*.in" "!./bin/**/*.part")))

(setf eldev-standard-excludes `(:or ,eldev-standard-excludes
                                    ;; Avoid including files in test "projects".
                                    (eldev-pcase-exhaustive eldev-test-type
                                                            (`main        "./test/*/")
                                                            (`integration '("./test/"   "!./test/integration"))
                                                            (`all         '("./test/*/" "!./test/integration")))
                                    "./webinstall"
                                    "./htmldoc"))

;; I don't want to "fix" docstrings, especially since they are mostly meant to be printed
;; to console in this project.
(with-eval-after-load 'bytecomp
  (setf byte-compile-warnings '(not docstrings)))


(defvar eldev-test-type 'main)

(eldev-defoption eldev-test-selection (type)
  "Select tests to run; type can be `main', `integration' or `all'"
  :options        (-T --test-type)
  :for-command    test
  :value          TYPE
  :default-value  eldev-test-type
  (unless (memq (intern type) '(main integration all))
    (signal 'eldev-wrong-option-usage `("unknown test type `%s'" ,type)))
  (setf eldev-test-type (intern type)))

(eldev-defcommand eldev-test-all (&rest parameters)
  "Same as `test --test-type=all'."
  :custom-parsing t
  :category       testing
  (let ((parsed (eldev-parse-command-line parameters :command 'test-all)))
    (eldev-test-selection "all")
    (apply #'eldev-test (plist-get parsed :without-options))))

(eldev-defcommand eldev-test-integration (&rest parameters)
  "Same as `test --test-type=integration'."
  :custom-parsing t
  :category       testing
  (let ((parsed (eldev-parse-command-line parameters :command 'test-integration)))
    (eldev-test-selection "integration")
    (apply #'eldev-test (plist-get parsed :without-options))))

(eldev-inherit-options 'test-all 'test (lambda (_option handler) (not (eq handler #'eldev-test-selection))))
(eldev-inherit-options 'test-integration 'test-all)

(add-hook 'eldev-test-hook
          (lambda ()
            (eldev-verbose "Using Eldev tests of type `%s'" eldev-test-type)
            (eldev-print "Eldev tests are fairly slow, since they spawn lots of child processes")))
(add-hook 'eldev-executing-command-hook
          (lambda (command)
            (unless (eq command 'test)
              ;; So that e.g. byte-compilation works on all tests.
              (setf eldev-test-type 'all))))


(eldev-defbuilder eldev-builder-preprocess-.in (source target)
  :short-name     "SUBST"
  :message        source-and-target
  :source-files   "*.in"
  :targets        (".in" -> "")
  :collect        ":default"
  :define-cleaner (eldev-cleaner-preprocessed
                   "Delete results of preprocessing `.in' files.  This is specific
to Eldev itself."
                   :aliases prep)
  (let ((modes (file-modes target)))
    (eldev-substitute source target)
    (when (or modes (string-prefix-p "bin/" target))
      (set-file-modes target (or modes #o755)))))


(eldev-defcleaner eldev-cleaner-test-caches ()
  "Delete `.eldev' directories in test projects.  This is specific
to Eldev itself."
  (let (directories)
    (dolist (project (directory-files "test"))
      (when (and (not (member project '("." ".." ".testroot"))) (file-directory-p (expand-file-name project "test")))
        (push (expand-file-name eldev-cache-dir (expand-file-name project "test")) directories)))
    directories))


(setf eldev-formatted-project-name "Eldev")
(setf eldev-release-post-release-commit-message "Post-release version bump."
      eldev-release-post-release-commit (lambda (version)
                                          (let ((eldev-release-min-version-size 3))
                                            (eldev-release-next-snapshot-version-unless-already-snapshot version))))

(defun eldev--require-future-doc-merged (_version)
  (eldev-call-process (eldev-git-executable)
      `("for-each-ref" "--no-merged=origin/master" "--format" "%(refname)")
    :die-on-error t
    ;; Catch both local and remote branches.  Need to push before releasing.
    (when (re-search-forward (rx bol (| "refs/heads/future-doc" "refs/remotes/origin/future-doc") eol) nil t)
      (eldev-release-maybe-fail (eldev-format-message "branch `future-doc' is not merged into `origin/master'")))))

;; Insert after after `eldev-release-validate-vcs'.
(push 'eldev--require-future-doc-merged (cdr (memq 'eldev-release-validate-vcs eldev-release-validators )))


(defmacro eldev--install-self-error (error directory format-string &rest arguments)
  `(signal 'eldev-error (append (unless (file-writable-p ,directory) '(:hint "You probably need to log in as root"))
                                (list ,format-string ,@arguments (error-message-string ,error)))))

(eldev-defcommand eldev-install-self (&rest parameters)
  "Install Eldev.  You only need this command if you want to
install from sources (clone of Git repository).  Should probably
be executed via `install.sh'."
  :parameters     "DIRECTORY"
  (unless parameters
    (eldev-error "Missing target directory")
    (when (boundp 'eldev--install-sh)
      (eldev-error "Run as `./install.sh DIRECTORY'"))
    (eldev-print "Suggested directories:")
    (dolist (directory '("~/bin" "~/.bin" "~/local/bin" "~/.local/bin" "/usr/local/bin" "/usr/bin"))
      (when (eldev-directory-in-exec-path directory)
        (eldev-print "  %s" directory)))
    (eldev-print "\nIf you use a different directory, make sure to add it to `PATH'\nenvironment variable.")
    (signal 'eldev-quit 1))
  (let ((directory         (car parameters))
        (shell-script-name (eldev--shell-script-name)))
    (if (file-exists-p directory)
        (unless (file-directory-p directory)
          (signal 'eldev-error `("File `%s' exists, but is not a directory" ,directory)))
      (if (eldev-y-or-n-p (eldev-format-message "Directory `%s' doesn't exist; attempt to create it? " directory))
          (condition-case error
              (make-directory directory t)
            (error (eldev--install-self-error error (file-name-directory directory) "When creating directory `%s': %s" directory)))
        (signal 'eldev-error `("Installation aborted"))))
    (when (file-exists-p (expand-file-name shell-script-name directory))
      (signal 'eldev-error `("File `%s' already exists in directory `%s'" ,shell-script-name ,directory)))
    ;; Unless verbosity level is set explicitly, make it `quiet' for
    ;; building process.
    (let ((eldev-verbosity-level (or eldev-verbosity-level 'quiet)))
      (eldev-build ":package"))
    (condition-case error
        (copy-file (format "bin/%s" shell-script-name)
                   (file-name-as-directory directory))
      (error (eldev--install-self-error error (file-name-as-directory directory) "When copying file `eldev' to directory `%s': %s" directory)))
    ;; Several duplicates with bootstrapping code, but I think it's
    ;; one of those rare cases where this is fine.
    (let* ((eldev--emacs-version (format "%s.%s" emacs-major-version emacs-minor-version))
           (package-user-dir     (expand-file-name "bootstrap" (expand-file-name eldev--emacs-version (eldev-global-cache-dir)))))
      (eldev-install-package-file (expand-file-name (format "dist/eldev-%s.tar" (eldev-message-version (eldev-package-descriptor))))))
    (eldev-print "Installation complete%s"
                 (if (eldev-directory-in-exec-path directory)
                     "; you can execute `eldev' now"
                   (format "\nDon't forget to add `%s' to `PATH' environment variable" directory)))))

;; Might want to convert this to a builder later.
(eldev-defcommand eldev-build-documentation (&rest parameters)
  "Generate documentation HTML from Asciidoctor source."
  :command        documentation
  :aliases        (doc html-doc)
  :category       building
  (when parameters
    (signal 'eldev-wrong-command-usage `(t "Unexpected command parameters")))
  (eldev-call-process "asciidoctor" `("--backend" "html5"
                                      "--destination-dir" "htmldoc"
                                      "--out-file" "index.html"
                                      ;; It seems to treat everything too lightly, so order it to fail on
                                      ;; anything even remotely potentially problematic.
                                      "--warnings" "--failure-level" "INFO"
                                      ,@(pcase eldev-verbosity-level
                                          (`quiet   '("--quiet"))
                                          (`verbose '("--verbose"))
                                          (`trace   '("--verbose")))
                                      ,@(when debug-on-error '("--trace"))
                                      "doc/README.adoc")
    :trace-command-line t
    :forward-output     t
    :die-on-error       t))
