<commit_before>(ns com.billpiel.sayid.deep-trace2
  (require [com.billpiel.sayid.util.other :as util]
           [com.billpiel.sayid.trace :as trace]))

(defn prs
  [v]
  (subs (with-out-str (clojure.pprint/pprint v)) 0 1000 ))

;; TODO exceptions

(def trace-fn-set #{`tr-if-ret `tr-if-clause `tr-macro})

(defn form->xform-map*
  [form]
  (if (seq? form)
    (let [x (macroexpand form)
          xx (clojure.walk/macroexpand-all form)] ;; TODO better way?
      (conj (mapcat form->xform-map* x)
            {form xx}))
    [{form form}]))

(defn form->xform-map
  [form]
  (apply merge (form->xform-map* form)))

(defn xform->form-map
  [form]
  (-> form
      form->xform-map
      clojure.set/map-invert))

(defn update-last
  [vctr f & args]
  (apply update-in
         vctr
         [(-> vctr
              count
              dec)]
         f
         args))

(defn path->sym
  [path]
  (->> path
       (clojure.string/join "_")
       (str "$")
       symbol))

(defn sym->path
  [sym]
  (util/$- -> sym
           name
           (subs 1)
           (clojure.string/split #"_")
           (remove #(= % "") $)
           (mapv #(Integer/parseInt %) $)))

(defn sym-seq->parent
  [syms]
  (util/$- -> syms
           first
           (if (coll? $)
             (sym-seq->parent $)
             $)
           sym->path
           drop-last
           path->sym))

(defn swap-in-path-syms*
  [form func parent path skip-macro?]
  (cond
    (and skip-macro?
         (util/macro? form)) form
    (coll? form)  (util/back-into form
                                  (doall (map-indexed #(swap-in-path-syms* %2
                                                                           func
                                                                           form
                                                                           (conj path %)
                                                                           skip-macro?)
                                                      form)))
    :else (func (->> path
                     (clojure.string/join "_")
                     (str "$")
                     symbol)
                path
                form
                parent)))

(defn swap-in-path-syms
  ([form func]
   (swap-in-path-syms* form
                       func
                       nil
                       []
                       false))
  ([form]
   (swap-in-path-syms form
                       #(first %&))))

(defn swap-in-path-syms-skip-macro
  [form]
  (swap-in-path-syms* form
                      #(first %&)
                      nil
                      []
                      true))

(defn deep-replace-symbols
  [smap coll]
  (clojure.walk/postwalk #(if (symbol? %)
                            (or (get smap %)
                                %)
                            %)
                         coll))

(defn get-path->form-maps
  [src]
  (let [sx-seq (->> src
                    (tree-seq coll? seq)
                    (filter coll?))
        pair-fn (fn [form]
                  (interleave (seq  form)
                              (repeat form)))]
    (apply hash-map
           (mapcat pair-fn
                   sx-seq))))

;;  xl     (-> src clojure.walk/macroexpand-all swap-in-path-syms)
;;  src   src
;;  oloc (-> src swap-in-path-syms clojure.walk/macroexpand-all)
;;  x-form (clojure.walk/macroexpand-all src)

;;  xloc->oloc (deep-zipmap (-> src clojure.walk/macroexpand-all swap-in-path-syms) (-> src swap-in-path-syms clojure.walk/macroexpand-all))
;;  xl->src  (deep-zipmap (-> src clojure.walk/macroexpand-all swap-in-path-syms) (clojure.walk/macroexpand-all src))
;;  ol->olop (-> src swap-in-path-syms get-path->form-maps)
;;  xl->xlxp (-> src clojure.walk/macroexpand-all swap-in-path-syms get-path->form-maps)
;;  ol->olxp (-> src swap-in-path-syms clojure.walk/macroexpand-all get-path->form-maps)
;;  xl->xp (deep-zipmap (-> src clojure.walk/macroexpand-all swap-in-path-syms) (clojure.walk/macroexpand-all src))
;;  olop->op (deep-zipmap (swap-in-path-syms src) src)

(defn mk-expr-mapping
  [form]
  (let [xls (->> form
                 clojure.walk/macroexpand-all
                 swap-in-path-syms
                 (tree-seq coll? seq))
        xloc->oloc (util/deep-zipmap (-> form clojure.walk/macroexpand-all swap-in-path-syms)
                                     (-> form swap-in-path-syms-skip-macro clojure.walk/macroexpand-all))
        oloc->xloc (clojure.set/map-invert xloc->oloc)
        xl->xform  (util/deep-zipmap (-> form clojure.walk/macroexpand-all swap-in-path-syms)
                                     (clojure.walk/macroexpand-all form))
        xform->form (xform->form-map form)
        ol->olop (-> form
                     swap-in-path-syms
                     get-path->form-maps)
        xl->xlxp (-> form
                     clojure.walk/macroexpand-all
                     swap-in-path-syms
                     get-path->form-maps)
        ol->olxp (-> form
                     swap-in-path-syms
                     clojure.walk/macroexpand-all
                     get-path->form-maps)
        xlxp->xp (util/deep-zipmap (-> form clojure.walk/macroexpand-all swap-in-path-syms)
                                   (clojure.walk/macroexpand-all form))
        olop->op (util/deep-zipmap (swap-in-path-syms form) form)
        f (fn [xl]
            {(if (coll? xl)
               (sym-seq->parent xl)
               xl)
             {:tree nil    ;; placeholder for trace tree
              :xl xl              ;; expanded location
              :orig (-> xl
                       xl->xform
                       xform->form)  ;; original symbol or value
              :x (-> xl
                     xl->xform)
              :xlxp (xl->xlxp xl) ;; expanded locations expanded parent
              :ol (xloc->oloc xl)
              :olop (-> xl
                        xloc->oloc
                        ol->olop)
              :xp  (-> xl
                       xl->xlxp
                       xlxp->xp)
              :op (-> xl
                      xloc->oloc
                      ol->olop
                      olop->op)
              :olxp (-> xl
                        xloc->oloc
                        ol->olxp)
              :xlop (-> xl
                        xloc->oloc
                        ol->olop
                        ((partial deep-replace-symbols oloc->xloc)))}})]
    (util/$- ->> xls
             (map f)
             (apply merge))))

(defn mk-tree-template
  [src-map fn-meta path parent-path & {:keys [macro?]}]
  (let [sub-src-map (-> #spy/d path
                        path->sym
                        src-map)
        form (if macro?
               (:orig sub-src-map)
               (:x sub-src-map))]
    {:parent-path parent-path
     :name (if (seq? form)
              (first form)
              form)
     :form form
     :macro? macro?
     :inner-path path
     :parent-name (:name fn-meta)
     :ns (-> fn-meta :ns str symbol)
     :xpanded-parent (:xp sub-src-map)
     :xpanded-frm (:x sub-src-map)}))

(defn get-temp-root-parent
  []
  (-> trace/*trace-log-parent*
      (select-keys [:depth :path])
      (assoc :children (atom [])
             :inner-path nil)
      atom))

(declare produce-recent-parent-tree-atom!)

(defn get-recent-tree-at-inner-path
  [path recent-trees]
  (let [entry (@recent-trees path)]
    (if (not (some->> entry
                      deref
                      keys
                      (some #{:return :throw})))
      entry
      nil)))

(defn assoc-to-recent-trees!
  [tree-atom parent-tree-atom recent-trees]
  (swap! recent-trees
         assoc
         (:inner-path @tree-atom)
         tree-atom)
  (when parent-tree-atom
    (println "vvvvv")
    #spy/d tree-atom
    #spy/d parent-tree-atom
    (println "^^^^^")
    (clojure.stacktrace/print-stack-trace (Exception. "assoc-to-recent-trees!"))
    (swap! (-> @parent-tree-atom
               :inner-path
               (@recent-trees)
               deref
               :children)
           conj
           tree-atom)))

(defn mk-recent-tree-at-inner-path
  [inner-path recent-trees]
  (if (nil? inner-path)
    (let [new-parent (get-temp-root-parent)]
      (assoc-to-recent-trees! new-parent
                              nil
                              recent-trees)
      new-parent)
    (let [new-grandparent (produce-recent-parent-tree-atom! inner-path
                                                            recent-trees)
          new-parent (-> (trace/mk-tree :parent new-grandparent)
                         (assoc :inner-path inner-path
                                :parent-path (:path new-grandparent))
                         atom)]
      (assoc-to-recent-trees! new-parent
                              new-grandparent
                              recent-trees)
      new-parent)))

(defn produce-recent-tree-atom!
  [inner-path recent-trees]
  (if-let [tree-atom (get-recent-tree-at-inner-path inner-path
                                                    recent-trees)]
    tree-atom
    (mk-recent-tree-at-inner-path inner-path
                                  recent-trees)))

(defn produce-recent-parent-tree-atom!
  [inner-path recent-trees]
  (let [parent-inner-path (some-> inner-path
                                  not-empty
                                  drop-last
                                  vec)]
    (if-let [parent (get-recent-tree-at-inner-path parent-inner-path
                                                   recent-trees)]
      parent
      (mk-recent-tree-at-inner-path parent-inner-path recent-trees))))

(defn tr-fn
  [inner-path [recent-trees src-map] template f]
  (fn [& args]
    (let [parent (produce-recent-parent-tree-atom! inner-path
                                                   recent-trees)
          this (-> (trace/mk-tree :parent @parent)
                   (merge template)
                   (assoc :args (vec args)
                          :arg-map (delay (zipmap (:arg-forms template)
                                                  args))
                          :started-at (trace/now)))
          [value throw] (binding [trace/*trace-log-parent* this]
                          (try
                            [(apply f args) nil]
                            (catch Throwable t
                              ;; TODO what's the best we can do here?
                              [nil (trace/Throwable->map** t)])))
          this' (assoc this
                       :return value
                       :throw throw ;;TODO not right
                       :ended-at (trace/now))]
      (assoc-to-recent-trees! #spy/d (atom this')
                              parent
                              recent-trees)
      value)))

(defn tr-macro
  [path [recent-trees] template mcro v]
  (def rt' @recent-trees)

  (let [tree-atom (produce-recent-tree-atom! path
                                             recent-trees)]

    (swap! (@recent-trees path)
           assoc
           :return v
           :inner-tags [:macro mcro])


    v))

(defn tr-if-ret
  [path [recent-trees] v]
  (swap! (produce-recent-tree-atom! path
                                    recent-trees)
         assoc
         :return v
         :inner-tags [:if])
  v)

(defn tr-if-clause
  [path [log] test v]
  (swap! log conj [path v :if test])
  (let [test-path (-> path
                      drop-last
                      vec
                      (conj 1))]
    (swap! log conj [test-path test :if]))
  v)

(declare xpand-form)

(defn xpand-all
  [form src-map fn-meta path parent-path]
  (when-not (nil? form)
    (util/back-into form
                    (doall (map-indexed #(xpand-form %2
                                                     src-map
                                                     fn-meta
                                                     (conj path %)
                                                     parent-path)
                                        form)))))

(defn xpand-fn-form
  [head form path template]
  (cons (list `tr-fn
              path
              '$$
              `'~template
              (first form))
        (rest form)))

(defn xpand-macro-form
  [head form path template]
  (list `tr-macro
        path
        '$$
        `'~template
        (keyword head)
        form))

(defn xpand-if-form
  [[_ test then else] path template]
  (list `tr-if-ret
        path
        '$$
        `'~template
        (concat ['if test
                 (list `tr-if-clause
                       (conj path 2)
                       '$$
                       `'~template
                       true
                       then)]
                (if-not (nil? else)
                  [(list `tr-if-clause
                         (conj path 3)
                         '$$
                         `'~template
                         false
                         else)]
                  []))))


(defn xpand-fn
  [head form src-map fn-meta path parent-path]
  #spy/d head
  #spy/d form
  #spy/d path
  #spy/d parent-path

  (xpand-fn-form head
                 (xpand-all form
                            src-map
                            fn-meta
                            path
                            path)
                 path
                 (mk-tree-template src-map
                                   fn-meta
                                   path
                                   parent-path)))

(defn xpand-macro
  [head form src-map fn-meta path parent-path]
  (xpand-macro-form head
                    (xpand-form (macroexpand form)
                                src-map
                                fn-meta
                                path
                                path)
                    path
                    (mk-tree-template src-map
                                      fn-meta
                                      path
                                      parent-path
                                      :macro? true)))

(defn xpand-if
  [form src-map fn-meta path parent-path]
  (xpand-if-form (xpand-all form
                            src-map
                            fn-meta
                            path
                            path)
                 path
                 (mk-tree-template src-map
                                   fn-meta
                                   path
                                   parent-path)))

(defn xpand-form
  [form src-map fn-meta & [path parent-path]]
  (let [path' (or path [])]
    (cond
      (seq? form)
      (let [head (first form)]
        (cond (util/macro? head)
              (xpand-macro head form src-map fn-meta path' parent-path)
              (= 'if head) (xpand-if form src-map fn-meta path' parent-path)
              (special-symbol? head) (xpand-all form src-map fn-meta path' parent-path)
              :else (xpand-fn head form src-map fn-meta path' parent-path)))

      (coll? form) form ;; TODO traverse
      :else form)))

;;TODO try-catch
(defn xpand
  [form parent-fn-meta]
  (let [expr-map (mk-expr-mapping form)
        xform (xpand-form form expr-map parent-fn-meta)]
    `(let [~'$$ [(atom {}) '~expr-map]
           ~'$return ~xform]
       (record-trace-tree ~'$$)
       ~'$return)))

#(do
   (def form1 '(-> 1 inc str))

   (-/p (xpand form1 {:name "name1" :ns "ns1"} ))

   (def form2 '(str (inc 1) 3))

   (-/p (xpand form2 {:name "name1" :ns "ns1"} ))

   (-/p (eval (xpand form1 {:name "name1" :ns "ns1"} )))

   (comment))

(defn xpand-bod
  [fn-bod parent-fn-meta]
  (cons (first fn-bod)
        (map #(xpand % parent-fn-meta)
             (rest fn-bod))))

(defn xpand-fn*
  [form parent-fn-meta]
  (let [bods (->> form
                  rest
                  (map #(xpand-bod % parent-fn-meta)))]
    (cons (first form)
          bods)))

(defn log->tree1
  [log src-map fn-meta]
  (loop [[[path val & tags] & tail] log
         agg {}]
    (if path
      (let [path' (interleave (repeat :children) path) ;; TODO the map won't preserve order
            sub-map (-> path
                        path->sym
                        src-map)
            agg' (update-in agg path'
                            #(merge %
                                    {:form (:orig sub-map)
                                     :return val
                                     :path path
                                     :tags tags
                                     :src-map sub-map
                                     :fn-meta fn-meta}))]
        (recur tail
               agg'))
      (-> agg
          (get nil)
          (assoc :children
                 (dissoc (:children agg)
                         nil))))))

(defn tree1->trace-tree
  [tree parent src-map]
  (let [{:keys [form return children tags fn-meta path]} tree
        trace-tree (if (-> tags
                           first
                           #{:fn})
                     (second tags)
                     (assoc (trace/mk-tree :parent parent)
                            :name (if (seq? form) (first form) form)
                            :form form
                            :path' path
                            :parent-name (:name fn-meta)
                            :ns (-> fn-meta :ns str symbol)
                            :args []                  ;;TODO
                            :arg-map {}               ;;TODO
                                        ; :src-map src-map
                            :started-at nil
                            :ended-at nil
                            :return return))]
    (assoc trace-tree
           :children
           (mapv #(-> %
                      second
                      (tree1->trace-tree trace-tree
                                         src-map))
                 children))))

#_ (defn record-trace-tree
  [[log src-map]]
  (swap! (:children trace/*trace-log-parent*)
         conj
         (-> @log
             (log->tree1 src-map fn-meta)
             (tree1->trace-tree trace/*trace-log-parent*
                                src-map))))

(defn record-trace-tree
  [[log src-map]]
  (-/p (@log nil))
  (def log' @log)
#_  (loop [[head & body] log
         tree-map {}
         mailbox {}]
    (if head
      (let [path (:path head)
            tree-tmplt (tree-tmplts path)
            inbox (mailbox path)
            tree (mk-tree )


            (assoc tree-map
                   path
                   ()
                   )])
      tree-map)))

(defn get-fn
  [[d s f & r]]
  (if (and (= d 'def)
           (symbol? s)<commit_msg>partial rewrite deep-trace2<commit_after>(ns com.billpiel.sayid.deep-trace2
  (require [com.billpiel.sayid.util.other :as util]
           [com.billpiel.sayid.trace :as trace]))

(defn prs
  [v]
  (subs (with-out-str (clojure.pprint/pprint v)) 0 1000 ))

;; TODO exceptions

(def trace-fn-set #{`tr-if-ret `tr-if-clause `tr-macro})

(defn form->xform-map*
  [form]
  (if (seq? form)
    (let [x (macroexpand form)
          xx (clojure.walk/macroexpand-all form)] ;; TODO better way?
      (conj (mapcat form->xform-map* x)
            {form xx}))
    [{form form}]))

(defn form->xform-map
  [form]
  (apply merge (form->xform-map* form)))

(defn xform->form-map
  [form]
  (-> form
      form->xform-map
      clojure.set/map-invert))

(defn update-last
  [vctr f & args]
  (apply update-in
         vctr
         [(-> vctr
              count
              dec)]
         f
         args))

(defn path->sym
  [path]
  (->> path
       (clojure.string/join "_")
       (str "$")
       symbol))

(defn sym->path
  [sym]
  (util/$- -> sym
           name
           (subs 1)
           (clojure.string/split #"_")
           (remove #(= % "") $)
           (mapv #(Integer/parseInt %) $)))

(defn sym-seq->parent
  [syms]
  (util/$- -> syms
           first
           (if (coll? $)
             (sym-seq->parent $)
             $)
           sym->path
           drop-last
           path->sym))

(defn swap-in-path-syms*
  [form func parent path skip-macro?]
  (cond
    (and skip-macro?
         (util/macro? form)) form
    (coll? form)  (util/back-into form
                                  (doall (map-indexed #(swap-in-path-syms* %2
                                                                           func
                                                                           form
                                                                           (conj path %)
                                                                           skip-macro?)
                                                      form)))
    :else (func (->> path
                     (clojure.string/join "_")
                     (str "$")
                     symbol)
                path
                form
                parent)))

(defn swap-in-path-syms
  ([form func]
   (swap-in-path-syms* form
                       func
                       nil
                       []
                       false))
  ([form]
   (swap-in-path-syms form
                       #(first %&))))

(defn swap-in-path-syms-skip-macro
  [form]
  (swap-in-path-syms* form
                      #(first %&)
                      nil
                      []
                      true))

(defn deep-replace-symbols
  [smap coll]
  (clojure.walk/postwalk #(if (symbol? %)
                            (or (get smap %)
                                %)
                            %)
                         coll))

(defn get-path->form-maps
  [src]
  (let [sx-seq (->> src
                    (tree-seq coll? seq)
                    (filter coll?))
        pair-fn (fn [form]
                  (interleave (seq  form)
                              (repeat form)))]
    (apply hash-map
           (mapcat pair-fn
                   sx-seq))))

;;  xl     (-> src clojure.walk/macroexpand-all swap-in-path-syms)
;;  src   src
;;  oloc (-> src swap-in-path-syms clojure.walk/macroexpand-all)
;;  x-form (clojure.walk/macroexpand-all src)

;;  xloc->oloc (deep-zipmap (-> src clojure.walk/macroexpand-all swap-in-path-syms) (-> src swap-in-path-syms clojure.walk/macroexpand-all))
;;  xl->src  (deep-zipmap (-> src clojure.walk/macroexpand-all swap-in-path-syms) (clojure.walk/macroexpand-all src))
;;  ol->olop (-> src swap-in-path-syms get-path->form-maps)
;;  xl->xlxp (-> src clojure.walk/macroexpand-all swap-in-path-syms get-path->form-maps)
;;  ol->olxp (-> src swap-in-path-syms clojure.walk/macroexpand-all get-path->form-maps)
;;  xl->xp (deep-zipmap (-> src clojure.walk/macroexpand-all swap-in-path-syms) (clojure.walk/macroexpand-all src))
;;  olop->op (deep-zipmap (swap-in-path-syms src) src)

(defn mk-expr-mapping
  [form]
  (let [xls (->> form
                 clojure.walk/macroexpand-all
                 swap-in-path-syms
                 (tree-seq coll? seq))
        xloc->oloc (util/deep-zipmap (-> form clojure.walk/macroexpand-all swap-in-path-syms)
                                     (-> form swap-in-path-syms-skip-macro clojure.walk/macroexpand-all))
        oloc->xloc (clojure.set/map-invert xloc->oloc)
        xl->xform  (util/deep-zipmap (-> form clojure.walk/macroexpand-all swap-in-path-syms)
                                     (clojure.walk/macroexpand-all form))
        xform->form (xform->form-map form)
        ol->olop (-> form
                     swap-in-path-syms
                     get-path->form-maps)
        xl->xlxp (-> form
                     clojure.walk/macroexpand-all
                     swap-in-path-syms
                     get-path->form-maps)
        ol->olxp (-> form
                     swap-in-path-syms
                     clojure.walk/macroexpand-all
                     get-path->form-maps)
        xlxp->xp (util/deep-zipmap (-> form clojure.walk/macroexpand-all swap-in-path-syms)
                                   (clojure.walk/macroexpand-all form))
        olop->op (util/deep-zipmap (swap-in-path-syms form) form)
        f (fn [xl]
            {(if (coll? xl)
               (sym-seq->parent xl)
               xl)
             {:tree nil    ;; placeholder for trace tree
              :xl xl              ;; expanded location
              :orig (-> xl
                       xl->xform
                       xform->form)  ;; original symbol or value
              :x (-> xl
                     xl->xform)
              :xlxp (xl->xlxp xl) ;; expanded locations expanded parent
              :ol (xloc->oloc xl)
              :olop (-> xl
                        xloc->oloc
                        ol->olop)
              :xp  (-> xl
                       xl->xlxp
                       xlxp->xp)
              :op (-> xl
                      xloc->oloc
                      ol->olop
                      olop->op)
              :olxp (-> xl
                        xloc->oloc
                        ol->olxp)
              :xlop (-> xl
                        xloc->oloc
                        ol->olop
                        ((partial deep-replace-symbols oloc->xloc)))}})]
    (util/$- ->> xls
             (map f)
             (apply merge))))

(defn mk-tree-template
  [src-map fn-meta path parent-path & {:keys [macro?]}]
  (let [sub-src-map (-> #spy/d path
                        path->sym
                        src-map)
        form (if macro?
               (:orig sub-src-map)
               (:x sub-src-map))]
    {:parent-path parent-path
     :name (if (seq? form)
              (first form)
              form)
     :form form
     :macro? macro?
     :inner-path path
     :parent-name (:name fn-meta)
     :ns (-> fn-meta :ns str symbol)
     :xpanded-parent (:xp sub-src-map)
     :xpanded-frm (:x sub-src-map)}))

(defn get-temp-root-tree
  []
  (-> trace/*trace-log-parent*
      (select-keys [:depth :path])
      (assoc :children (atom [])
             :inner-path nil)
      atom))

(declare produce-recent-parent-tree-atom!)

(defn get-recent-tree-at-inner-path
  [path recent-trees & {:keys [skip-closed-check]}]
  (let [entry (@recent-trees path)]
    (if (or skip-closed-check
            (not (some->> entry
                          deref
                          keys
                          (some #{:return :throw}))))
      entry
      nil)))

(defn update-tree!
  [tree recent-trees]
  #spy/d @recent-trees
  (reset! #spy/d (-> tree
                     :inner-path
                     (@recent-trees))
          tree))

(defn conj-to-parent!
  [tree-atom parent-atom]
  (swap! (:children @parent-atom)
         conj
         tree-atom))

(defn push-to-recent-trees!
  [tree-atom recent-trees]
  (swap! recent-trees
         assoc
         (:inner-path @tree-atom)
         tree-atom)
  #spy/d @recent-trees)

(defn mk-recent-tree-at-inner-path
  [inner-path recent-trees]
  (if (nil? inner-path)
    (let [new-tree (get-temp-root-tree)]
      (push-to-recent-trees!  new-tree
                              recent-trees)
      new-tree)
    (let [parent (produce-recent-parent-tree-atom! inner-path
                                                   recent-trees)
          new-tree (-> (trace/mk-tree :parent @parent)
                       (assoc :inner-path inner-path
                              :parent-path (:path @parent))
                       atom)]
      (push-to-recent-trees! new-tree
                             recent-trees)
      (conj-to-parent! new-tree
                       parent)
      new-tree)))

(defn produce-recent-tree-atom!
  [inner-path recent-trees & {:keys [skip-closed-check]}]
  (if-let [tree-atom (get-recent-tree-at-inner-path inner-path
                                                         recent-trees
                                                         :skip-closed-check skip-closed-check)]
    tree-atom
    (mk-recent-tree-at-inner-path inner-path
                                  recent-trees)))

(defn produce-recent-parent-tree-atom!
  [inner-path recent-trees]
  (let [parent-inner-path (some-> inner-path
                                  not-empty
                                  drop-last
                                  vec)]
    (if-let [parent (get-recent-tree-at-inner-path parent-inner-path
                                                   recent-trees)]
      parent
      (mk-recent-tree-at-inner-path parent-inner-path recent-trees))))

(defn tr-fn
  [inner-path [recent-trees src-map] template f]
  (fn [& args]
    (let [this (-> @(produce-recent-tree-atom! #spy/d inner-path
                                               recent-trees)
                   (merge #spy/d template)
                   (assoc :args (vec args)
                          :arg-map (delay (zipmap (:arg-forms template)
                                                  args))
                          :started-at (trace/now)))
          _ (update-tree! this recent-trees)
          [value throw] (binding [trace/*trace-log-parent* this]
                          (try
                            [(apply f args) nil]
                            (catch Throwable t
                              ;; TODO what's the best we can do here?
                              [nil (trace/Throwable->map** t)])))
          this' (assoc this
                       :return value
                       :throw throw ;;TODO not right
                       :ended-at (trace/now))]
      (update-tree! this'
                    recent-trees)
      value)))

(defn tr-macro
  [path [recent-trees] template mcro v]
  (def rt' @recent-trees)

  (let [tree @(produce-recent-tree-atom! path
                                         recent-trees
                                         :skip-closed-check true)]
    (-> tree
        (assoc :return v
               :inner-tags [:macro mcro])
        (merge template)
        (update-tree! recent-trees))
    v))

(defn tr-if-ret
  [path [recent-trees] template v]
  (-> path
      (produce-recent-tree-atom! recent-trees)
      deref
      (merge template)
      (assoc :return v
             :inner-tags [:if])
      (update-tree! recent-trees))
  v)

(defn tr-if-test
  [path [recent-trees] v]
  (-> path
      (produce-recent-parent-tree-atom! recent-trees)
      deref
      (assoc :args [v])
      (update-tree! recent-trees))
  v)

(defn tr-if-branch
  [path [recent-trees] test v]
  (-> path
      (produce-recent-parent-tree-atom! recent-trees)
      deref
      (assoc-in [:args 1] v)
      (update-tree! recent-trees))
  v)

(declare xpand-form)

(defn xpand-all
  [form src-map fn-meta path parent-path]
  (when-not (nil? form)
    (util/back-into form
                    (doall (map-indexed #(xpand-form %2
                                                     src-map
                                                     fn-meta
                                                     (conj path %)
                                                     parent-path)
                                        form)))))

(defn xpand-fn-form
  [head form path template]
  (cons (list `tr-fn
              path
              '$$
              `'~template
              (first form))
        (rest form)))

(defn xpand-macro-form
  [head form path template]
  (list `tr-macro
        path
        '$$
        `'~template
        (keyword head)
        form))

(defn xpand-if-form
  [[_ test then else] path template]
  (let [template' (assoc template
                         :test-form `'~test)]
    `(tr-if-ret ~path
                ~'$$
                '~template'
                (if (tr-if-test ~(conj path 1)
                                ~'$$
                                ~test)
                  (tr-if-branch ~(conj path 2)
                                ~'$$
                                true
                                ~then)
                  ~(when-not (nil? else)
                     `(tr-if-branch ~(conj path 3)
                                ~'$$
                                false
                                ~else))))))

(defn xpand-fn
  [head form src-map fn-meta path parent-path]
  #spy/d head
  #spy/d form
  #spy/d path
  #spy/d parent-path

  (xpand-fn-form head
                 (xpand-all form
                            src-map
                            fn-meta
                            path
                            path)
                 path
                 (mk-tree-template src-map
                                   fn-meta
                                   path
                                   parent-path)))

(defn xpand-macro
  [head form src-map fn-meta path parent-path]
  (xpand-macro-form head
                    (xpand-form (macroexpand form)
                                src-map
                                fn-meta
                                path
                                path)
                    path
                    (mk-tree-template src-map
                                      fn-meta
                                      path
                                      parent-path
                                      :macro? true)))

(defn xpand-if
  [form src-map fn-meta path parent-path]
  (xpand-if-form (xpand-all form
                            src-map
                            fn-meta
                            path
                            path)
                 path
                 (mk-tree-template src-map
                                   fn-meta
                                   path
                                   parent-path)))

(defn xpand-form
  [form src-map fn-meta & [path parent-path]]
  (let [path' (or path [])]
    (cond
      (seq? form)
      (let [head (first form)]
        (cond (util/macro? head)
              (xpand-macro head form src-map fn-meta path' parent-path)
              (= 'if head) (xpand-if form src-map fn-meta path' parent-path)
              (special-symbol? head) (xpand-all form src-map fn-meta path' parent-path)
              :else (xpand-fn head form src-map fn-meta path' parent-path)))

      (coll? form) form ;; TODO traverse
      :else form)))

;;TODO try-catch
(defn xpand
  [form parent-fn-meta]
  (let [expr-map (mk-expr-mapping form)
        xform (xpand-form form expr-map parent-fn-meta)]
    `(let [~'$$ [(atom {}) '~expr-map]
           ~'$return ~xform]
       (record-trace-tree ~'$$)
       ~'$return)))

#(do
   (def form1 '(-> 1 inc str))

   (-/p (xpand form1 {:name "name1" :ns "ns1"} ))

   (-/p (eval (xpand form1 {:name "name1" :ns "ns1"} )))

   (def form2 '(str (inc 1) 3))

   (-/p (xpand form2 {:name "name1" :ns "ns1"} ))

   (-/p (eval (xpand form2 {:name "name1" :ns "ns1"} )))

   (def form3 '(if 1 2 3))

   (-/p (xpand form3 {:name "name1" :ns "ns1"} ))

   (-/p (eval (xpand form3 {:name "name1" :ns "ns1"} )))


   (comment))

(defn xpand-bod
  [fn-bod parent-fn-meta]
  (cons (first fn-bod)
        (map #(xpand % parent-fn-meta)
             (rest fn-bod))))

(defn xpand-fn*
  [form parent-fn-meta]
  (let [bods (->> form
                  rest
                  (map #(xpand-bod % parent-fn-meta)))]
    (cons (first form)
          bods)))

(defn log->tree1
  [log src-map fn-meta]
  (loop [[[path val & tags] & tail] log
         agg {}]
    (if path
      (let [path' (interleave (repeat :children) path) ;; TODO the map won't preserve order
            sub-map (-> path
                        path->sym
                        src-map)
            agg' (update-in agg path'
                            #(merge %
                                    {:form (:orig sub-map)
                                     :return val
                                     :path path
                                     :tags tags
                                     :src-map sub-map
                                     :fn-meta fn-meta}))]
        (recur tail
               agg'))
      (-> agg
          (get nil)
          (assoc :children
                 (dissoc (:children agg)
                         nil))))))

(defn tree1->trace-tree
  [tree parent src-map]
  (let [{:keys [form return children tags fn-meta path]} tree
        trace-tree (if (-> tags
                           first
                           #{:fn})
                     (second tags)
                     (assoc (trace/mk-tree :parent parent)
                            :name (if (seq? form) (first form) form)
                            :form form
                            :path' path
                            :parent-name (:name fn-meta)
                            :ns (-> fn-meta :ns str symbol)
                            :args []                  ;;TODO
                            :arg-map {}               ;;TODO
                                        ; :src-map src-map
                            :started-at nil
                            :ended-at nil
                            :return return))]
    (assoc trace-tree
           :children
           (mapv #(-> %
                      second
                      (tree1->trace-tree trace-tree
                                         src-map))
                 children))))

#_ (defn record-trace-tree
  [[log src-map]]
  (swap! (:children trace/*trace-log-parent*)
         conj
         (-> @log
             (log->tree1 src-map fn-meta)
             (tree1->trace-tree trace/*trace-log-parent*
                                src-map))))

(defn record-trace-tree
  [[log src-map]]
  #spy/d (@log nil)
  (def log' @log)
#_  (loop [[head & body] log
         tree-map {}
         mailbox {}]
    (if head
      (let [path (:path head)
            tree-tmplt (tree-tmplts path)
            inbox (mailbox path)
            tree (mk-tree )


            (assoc tree-map
                   path
                   ()
                   )])
      tree-map)))

(defn get-fn
  [[d s f & r]]
  (if (and (= d 'def)
           (symbol? s)
{'fraction_non_alphanumeric': 0.12369636963696369, 'fraction_numerical': 0.0020330033003300332, 'mean_word_length': 1.0124329206737155, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 244, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 139, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2123964', 'n_tokens_mistral': 11505, 'n_tokens_neox': 10845, 'n_words': 2445}