(ns buddho.core.state.property
  (:require [clojure.set :refer [intersection]]
            [buddho.core.state.protocols :refer [value reference]]
            [buddho.core.state.memory :refer [state]]))

(defn properties [idea]
  (:_properties (value idea)))

(defn with-property [property]
  (get-in @state (concat [:_properties] property [:_node])))

(defn has-property? [idea property]
  (contains? (properties idea) property))

(defn register-property! [idea & properties]
  (let [property (first properties)
        tail (rest properties)
        iid (reference idea)]
    (when property
      (reduce (fn [acc property]
                (swap! state update-in (conj acc property :_node) (fnil conj #{}) iid)
                (swap! state update-in [iid :_properties] (fnil conj #{}) (conj (vec (rest acc)) property))
                (conj acc property))
              [:_properties] property))
    (when tail
      (run! #(register-property! idea %) tail))
    idea))

(defn- remove-property-node! [idea property]
  (swap! state update-in
         (concat [:_properties] property [:_node])
         disj (reference idea)))

(defn child-properties [property]
  (map #(conj property %)
       (-> @state :_properties (get-in property)
           keys set (disj :_node))))

(defn- rm-prop! [idea property]
  (let [;; XXX can also be done using prefix checking if slow
        children (intersection
                  (set (child-properties property))
                  (properties idea))]
    (swap! state update-in [idea :_properties] disj property)
    (remove-property-node! idea property)
    (run! #(rm-prop! idea %) children)))

(defn remove-property! [idea & properties]
  (let [property (first properties)
        tail (rest properties)
        iid (reference idea)]
    (when property
      (rm-prop! iid property))
    (when tail
      (run! #(rm-prop! iid %) tail))))
