<commit_before>(ns untangled.state
  (:require [untangled.events :as evt]
            cljs.pprint
            [untangled.application :as app]
            [untangled.history :as h]))

(defn root-context
  "Create a root context for a given Untangled Application (see untangled.core/new-application)."
  [app]
  {
   :application     app
   :scope           []
   :event-listeners []
   }
  )

(defn- find-first [pred coll] (first (filter pred coll)))

(defn log [message] (.log js/console message))


(defn resolve-data-path [state path-seq]
  (reduce (fn [real-path path-ele]
            (if (sequential? path-ele)
              (do
                (if (not= 3 (count path-ele))
                  (log "ERROR: VECTOR BASED DATA ACCESS MUST HAVE A 3-TUPLE KEY")
                  (let [vector-key (first path-ele)
                        state-vector (get-in state (conj real-path vector-key))
                        lookup-function (second path-ele)
                        target-value (nth path-ele 2)
                        index (->> (map-indexed vector state-vector) (find-first #(= target-value (lookup-function (second %)))) (first))
                        ]
                    (if index
                      (conj real-path vector-key index)
                      (do
                        (log "ERROR: NO ITEM FOUND AT DATA PATH")
                        (cljs.pprint/pprint path-seq)
                        real-path
                        )
                      )
                    )))
              (conj real-path path-ele)
              )
            )
          [] path-seq))

(defn data-path
  "Used by internal context tracking to correct the internal path to account for the inclusion of vectors as data structures 
  pointing to items in vectors."
  [context]
  (let [state @(-> context :application :app-state)
        path-seq (:scope context)
        ]
    (resolve-data-path state path-seq)
    ))

(defn context-data
  "Extract the data for the component indicated by the given context."
  [context]
  (let [state-atom (-> context :application :app-state)
        path (data-path context)
        ]
    (get-in @state-atom path)))

(defn update-in-context
  "Update the application state by applying the given operation to the state of the component implied by
  the given context. Think of this as a 'targeted' `swap!` where you don't have to know where the data is 
  stored. This function also records the change in this state history with an optional associated Reason."
  [context operation undoable compressable reason]
  (let [application (-> context :application)
        state-atom (:app-state application)
        history-atom (:history application)
        path (data-path context)
        old-state @state-atom
        history-entry (h/set-reason (h/new-point-in-time old-state undoable compressable) reason)]
    (swap! history-atom #(h/record % history-entry))
    (swap! state-atom #(update-in % path operation))
    (swap! state-atom #(assoc % :time (h/now)))
    (app/state-changed application old-state @state-atom)<commit_msg>fixed time stamping so it happens on the same swap as the state change, so dupe triggers of atom watchers do not happen<commit_after>(ns untangled.state
  (:require [untangled.events :as evt]
            cljs.pprint
            [untangled.application :as app]
            [untangled.history :as h]))

(defn root-context
  "Create a root context for a given Untangled Application (see untangled.core/new-application)."
  [app]
  {
   :application     app
   :scope           []
   :event-listeners []
   }
  )

(defn- find-first [pred coll] (first (filter pred coll)))

(defn log [message] (.log js/console message))


(defn resolve-data-path [state path-seq]
  (reduce (fn [real-path path-ele]
            (if (sequential? path-ele)
              (do
                (if (not= 3 (count path-ele))
                  (log "ERROR: VECTOR BASED DATA ACCESS MUST HAVE A 3-TUPLE KEY")
                  (let [vector-key (first path-ele)
                        state-vector (get-in state (conj real-path vector-key))
                        lookup-function (second path-ele)
                        target-value (nth path-ele 2)
                        index (->> (map-indexed vector state-vector) (find-first #(= target-value (lookup-function (second %)))) (first))
                        ]
                    (if index
                      (conj real-path vector-key index)
                      (do
                        (log "ERROR: NO ITEM FOUND AT DATA PATH")
                        (cljs.pprint/pprint path-seq)
                        real-path
                        )
                      )
                    )))
              (conj real-path path-ele)
              )
            )
          [] path-seq))

(defn data-path
  "Used by internal context tracking to correct the internal path to account for the inclusion of vectors as data structures 
  pointing to items in vectors."
  [context]
  (let [state @(-> context :application :app-state)
        path-seq (:scope context)
        ]
    (resolve-data-path state path-seq)
    ))

(defn context-data
  "Extract the data for the component indicated by the given context."
  [context]
  (let [state-atom (-> context :application :app-state)
        path (data-path context)
        ]
    (get-in @state-atom path)))

(defn update-in-context
  "Update the application state by applying the given operation to the state of the component implied by
  the given context. Think of this as a 'targeted' `swap!` where you don't have to know where the data is 
  stored. This function also records the change in this state history with an optional associated Reason."
  [context operation undoable compressable reason]
  (let [application (-> context :application)
        state-atom (:app-state application)
        history-atom (:history application)
        path (data-path context)
        old-state @state-atom
        history-entry (h/set-reason (h/new-point-in-time old-state undoable compressable) reason)]
    (swap! history-atom #(h/record % history-entry))
    (swap! state-atom (fn [old-state]
                        (-> old-state
                            (assoc :time (h/now)) 
                            (update-in path operation))))
    (app/state-changed application old-state @state-atom)
{'fraction_non_alphanumeric': 0.10154282115869018, 'fraction_numerical': 0.0009445843828715365, 'mean_word_length': 1.856564748201439, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 14, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6506158', 'n_tokens_mistral': 1790, 'n_tokens_neox': 1717, 'n_words': 592}