 
 ;; TODO better interop by coercing argument types
 
-(defn- method-specs [^java.lang.Class c]
+(defn- method-specs
+  "Creates a seq of specs for all public methods of the provided class."
+  [^java.lang.Class c]
   (->> c
        .getDeclaredMethods
        (filter is-public)

           @~instance
           result#))))))

(defn build-function
  "TODO make private"
  [target-sym no-currying type-hinting [fn-name specs]]
  (let [fn-symbol (symbol fn-name)
        use-type-hinting type-hinting
        arity-fn (partial build-arity target-sym no-currying use-type-hinting)]
    `(defn ~fn-symbol ~@(map arity-fn specs))))

(defn- define-symbol

               :param-types (.getParameterTypes m)
               :is-static (java.lang.reflect.Modifier/isStatic (.getModifiers m))}))))

(defn- hint-symbol
  "Hints the provided symbol using the provided type.
  For primitive types uses the Java wrapper classes."
  [sym type]
  (let [boxed-primitive {"int" java.lang.Integer "long" java.lang.Long
                         "short" java.lang.Short "float" java.lang.Float
                         "double" java.lang.Double "boolean" java.lang.Boolean
                         "byte" java.lang.Byte "char" java.lang.Character}]
    (vary-meta sym assoc :tag (or (boxed-primitive (.getName type)) type))))

(defn- build-params
  "Given a method spec returns a list of symbols to be used as function
  parameters (type-hinting them if necessary)"
  [spec use-type-hinting]
  (let [params (->> spec
                    :param-count
                    range
                    (map gensym)
                    vec)]
    (if (and use-type-hinting (not (:disable-hinting spec)))
      (map hint-symbol params (:param-types spec))
      params)))

(defn- build-arity
  "Giving a spec builds the arity part of a fn, i.e.: `([params] body)."
  [instance no-currying use-type-hinting spec]
  (let [raw-method-symbol (symbol (str "." (:raw-name spec)))
        params (build-params spec use-type-hinting)
        is-void (:is-void spec)]
    (cond
     ;; arity for static method
     (:is-static spec)
     (let [static-method-symbol (symbol (str (:class-name spec) "/" (:raw-name spec)))]
       `([~@params] (~static-method-symbol ~@params)))
     ;; arity for non-curried function
     no-currying
     `([this# ~@params]
       (let [result# (~raw-method-symbol this# ~@params)]
         (if ~is-void
           this#
           result#)))
     ;; arity for curried function
     :else
     `([~@params]
       (let [result# (~raw-method-symbol @~instance ~@params)]
