 (defn class-info-from-file
   "As per class-file, but takes a filename as a string."
   [^String filename]
-  (with-open [class-input-stream (new java.io.FileInputStream filename)]
-    (class-info class-input-stream)))+  (with-open [class-input-stream (java.io.BufferedInputStream. (java.io.FileInputStream. filename))]
+    (class-info class-input-stream)))

                                 })

(defn- fix-type-name
  [^String name]
  (.replaceAll (.replaceAll name "/" ".") "\\[\\]" ""))

(defn- fix-field-descriptor
  [^String field-descriptor]
  (fix-type-name (.getClassName (org.objectweb.asm.Type/getType field-descriptor))))

(defn- add-dependency
  [dependencies dependency type]
  (if (contains? dependencies dependency)
    (let [existing-dependency-types (dependencies dependency)
          new-dependency-types      (conj existing-dependency-types type)]
      (merge dependencies {dependency new-dependency-types}))
    (merge dependencies {dependency #{type}})))

(defn- add-dependencies
  [dependencies new-dependencies type]
  (if (or (empty? new-dependencies) (nil? new-dependencies))
    dependencies
    (loop [result                 dependencies
           current-dependency     (first new-dependencies)
           remaining-dependencies (rest  new-dependencies)]
      (if (empty? remaining-dependencies)
        (add-dependency result current-dependency type)
        (recur (add-dependency result current-dependency type) (first remaining-dependencies) (rest remaining-dependencies))))))

(defn- typeof
  [access]
  (cond
    (not= 0 (bit-and access org.objectweb.asm.Opcodes/ACC_INTERFACE))  :interface
    (not= 0 (bit-and access org.objectweb.asm.Opcodes/ACC_ENUM))       :enum
    (not= 0 (bit-and access org.objectweb.asm.Opcodes/ACC_ANNOTATION)) :annotation
    :else                                                              :class))

(defn- visit
  [class-info version access class-name signature super-name interfaces]
  (let [fixed-class-name      (fix-type-name class-name)
        fixed-super-name      (fix-type-name super-name)
        fixed-interface-names (vec (map fix-type-name interfaces))
        existing-dependencies (:dependencies class-info)]
    (merge class-info
           { :name              fixed-class-name
             :type              (typeof access)
             :class-version     version
             :class-version-str (version-name-map version)
             :dependencies      (add-dependency (add-dependencies existing-dependencies fixed-interface-names :implements)
                                                fixed-super-name :extends) } )))

(defn- visit-field
  [class-info access field-name desc signature value]
  (merge class-info
         {
           :dependencies (add-dependency (:dependencies class-info) (fix-field-descriptor desc) :uses)
         }))

(defn- visit-annotation
  [class-info name desc]
  (merge class-info
         {
           ;####TODO!!!!
         }))

(defn- visit-method
  [class-info access name ^String desc signature exceptions]
  (let [fixed-exception-names (vec (map fix-type-name exceptions))
        existing-dependencies (:dependencies class-info)
        argument-types        (vec (map #(fix-type-name (.getClassName %)) (org.objectweb.asm.Type/getArgumentTypes desc)))

    :type                  :class OR :interface OR :annotation OR :enum
    :class-version         49
    :class-version-str     \"1.5\"
    :dependencies          {\"dependentTypeName\" #{:extends :implements :uses :inner-class :parent-class}
                            ...}
  }

