    [org.dada.core counted-set utils]
    )
   (:import
-   [java.util Collection LinkedHashMap Map]
+   [java.util Collection HashMap Map]
    [org.dada.core Attribute Data Getter Metadata Metadata$VersionComparator Model RemoteModel Tuple Update View]
    )
   (:gen-class

;;------------------------------------------------------------------------------

(defn ^{:private true} -writeReplace [^org.dada.core.JoinModel this]
  (let [[_mutable name metadata] (.state this)]
      (RemoteModel. name metadata)))

;;--------------------------------------------------------------------------------

(defn -post-init [^org.dada.core.JoinModel self ^String model-name _ ^Model lhs-model rhses join-fn]
  (let [[mutable immutable] (.state self)
	rhs-model-to-lhs-fks (invert-map rhses)
	lhs-metadata (.getMetadata lhs-model)
	get-lhs-getter (fn [key] (.getGetter (.getAttribute lhs-metadata key)))
	i-to-rhs-model-and-lhs-getters (into
					(sorted-map)
					(map
					 (fn [i [^Model model keys]]
                                           [i [model (map get-lhs-getter keys)]])
					 (range)
					 rhs-model-to-lhs-fks))
	i-to-lhs-getters (into (sorted-map) (map (fn [[i [model getters]]] [i getters]) i-to-rhs-model-and-lhs-getters))]
    
    (dorun
     (map
      (fn [[i [^Model rhs-model lhs-getters]]]
        (trace ["post-init: watching rhs:" i rhs-model])
        (let [^Metadata rhs-metadata (.getMetadata rhs-model)
              rhs-pk-getter (.getPrimaryGetter rhs-metadata)
              rhs-version-comparator (.getVersionComparator rhs-metadata)
              ^Data data
              (.attach
               rhs-model
               (proxy [View] []
                 (update [insertions alterations deletions]
                   (let [[_ _ _ insertions alterations deletions]
                         (swap! mutable update-rhs insertions alterations deletions i rhs-pk-getter rhs-version-comparator lhs-getters join-fn)]
                     (if (or insertions alterations deletions)
                       (notifyUpdate self insertions alterations deletions))))
                 (toString [] (print-object this (str "Proxy:" model-name)))))]
          (swap! mutable update-rhs
                 (map (fn [extant] (Update. nil extant))(.getExtant data))
                 nil nil i rhs-pk-getter rhs-version-comparator lhs-getters join-fn)))
      i-to-rhs-model-and-lhs-getters))

    (trace ["post-init: watching lhs:" lhs-model])
    (let [initial-rhs-refs (apply vector (map (fn [[k vs]] (apply vector (map (fn [v] nil) vs))) rhs-model-to-lhs-fks))
	  ^Metadata lhs-metadata (.getMetadata lhs-model)
	  lhs-pk-getter (.getPrimaryGetter lhs-metadata)
	  lhs-version-comparator (.getVersionComparator lhs-metadata)
	  ^Data data
	  (.attach
	   lhs-model
	   (proxy [View] []
             (update [insertions alterations deletions]
               (let [[_ _ _ insertions alterations deletions]
                     (swap! mutable update-lhs insertions alterations deletions lhs-pk-getter lhs-version-comparator i-to-lhs-getters join-fn initial-rhs-refs)]
                 (if (or insertions alterations deletions)
                   (notifyUpdate self insertions alterations deletions))))))]
      (swap! mutable update-lhs
	     (map (fn [extant] (Update. nil extant))(.getExtant data))
	     nil nil lhs-pk-getter lhs-version-comparator i-to-lhs-getters join-fn initial-rhs-refs) ;; TODO - deletions/extinct
      )))

(defn ^String -toString [^org.dada.core.JoinModel this]
  (let [[_ name] (.state this)]

   (fn [[old-lhs-index old-insertions old-alterations old-deletions] [j lhs-pks]]
     (reduce
      (fn [[old-lhs-index old-insertions old-alterations old-deletions] lhs-pk]
	(let [^LHSEntry old-lhs-entry (map-get old-lhs-index lhs-pk)
	      old-lhs-rhs-refs (.rhs-refs old-lhs-entry)
	      new-lhs-rhs-refs (assoc old-lhs-rhs-refs i (assoc (old-lhs-rhs-refs i) j new-rhs))
	      new-lhs-version (inc (.version old-lhs-entry))
