(in-package :abcl-asdf)

(defvar *added-to-classpath* nil)

(defvar *inhibit-add-to-classpath* nil)

(defun add-directory-jars-to-class-path (directory recursive-p)
  (loop :for jar :in (if recursive-p 
                         (all-jars-below directory) 
                         (directory (merge-pathnames "*.jar" directory)))
     :do (java:add-to-classpath jar)))

(defun all-jars-below (directory) 
  (loop :with q = (system:list-directory directory) 
     :while q :for top = (pop q)
     :if (null (pathname-name top)) 
     :do (setq q (append q (all-jars-below top))) 
     :if (equal (pathname-type top) "jar") 
     :collect top))

(defun need-to-add-directory-jar? (directory recursive-p)
  (loop :for jar :in (if recursive-p 
                         (all-jars-below directory)
                         (directory (merge-pathnames "*.jar" directory)))
     :doing (if (not (member (namestring (truename jar)) 
                             *added-to-classpath* :test 'equal))
                (return-from need-to-add-directory-jar? t)))
  nil)

(defmethod java:add-to-classpath :around ((uri-or-uris t) &optional classloader)
  (declare (ignore classloader))
  (call-next-method)
  (if (listp uri-or-uris)
      (dolist (uri uri-or-uris)
        (pushnew uri *added-to-classpath*))
      (pushnew uri-or-uris *added-to-classpath*)))

(in-package :asdf)

(defclass jar-directory (static-file) ())

(defmethod perform ((operation compile-op) (c jar-directory))
  (unless abcl-asdf:*inhibit-add-to-classpath*
    (abcl-asdf:add-directory-jars-to-class-path (truename (component-pathname c)) t)))

(defmethod perform ((operation load-op) (c jar-directory))
  (unless abcl-asdf:*inhibit-add-to-classpath*
    (abcl-asdf:add-directory-jars-to-class-path (truename (component-pathname c)) t)))

(defmethod operation-done-p ((operation load-op) (c jar-directory))
  (or abcl-asdf:*inhibit-add-to-classpath*
      (not (abcl-asdf:need-to-add-directory-jar? (component-pathname c) t))))

(defmethod operation-done-p ((operation compile-op) (c jar-directory))
  t)

(defclass jar-file (static-file)
  ((type :initform "jar")))

(defmethod perform ((operation compile-op) (c jar-file))
  (java:add-to-classpath (component-pathname c)))

(defmethod perform ((operation load-op) (c jar-file))
  (or abcl-asdf:*inhibit-add-to-classpath*
      (java:add-to-classpath (component-pathname c))))

;;; The original JSS specified jar pathnames as having a NAME ending
;;; in ".jar" without a TYPE.  If we encounter such a definition, we
;;; clean it up.
(defmethod normalize-jar-name ((component jar-file))
  (when (#"endsWith" (slot-value component 'name) ".jar")
    (with-slots (name absolute-pathname) component
      (let* ((new-name 
              (subseq name 0 (- (length name) 4)))
             (new-absolute-pathname 
              (make-pathname :defaults absolute-pathname :name new-name)))
        (setf name new-name
              absolute-pathname new-absolute-pathname)))))

(defmethod perform :before ((operation compile-op) (c jar-file))
  (normalize-jar-name c))

(defmethod perform :before ((operation load-op) (c jar-file))
  (normalize-jar-name c))

(defmethod operation-done-p :before ((operation load-op) (c jar-file))
  (normalize-jar-name c))

(defmethod operation-done-p ((operation load-op) (c jar-file))
  (or abcl-asdf:*inhibit-add-to-classpath*
      (member (namestring (truename (component-pathname c)))
              abcl-asdf:*added-to-classpath* :test 'equal)))

(defmethod operation-done-p ((operation compile-op) (c jar-file))
  t)

(defclass class-file-directory (static-file) ())

(defmethod perform ((operation compile-op) (c class-file-directory))
  (java:add-to-classpath (component-pathname c)))

(defmethod perform ((operation load-op) (c class-file-directory))
  (java:add-to-classpath (component-pathname c)))

;; a jar file where the pathname and name are relative to JAVA_HOME
(defclass jdk-jar (jar-file) ())

(defmethod normalize-jar-name :after ((c jdk-jar))
  (setf (slot-value c 'absolute-pathname)
	(merge-pathnames
	 (merge-pathnames 
	  (slot-value c 'name)
	  (make-pathname :directory `(:relative ,(slot-value (component-parent c) 'relative-pathname))))
	 (java::jstatic "getProperty" "java.lang.System" "java.home"))))


		   
