(ns buddho.core.dhamma.aspects
  "Facilities for querying and manipulating dhamma aspects."
  (:require [buddho.core.state :as s :refer [value reference]]
            [buddho.core.dhamma.data :refer [mutate]]
            [clojure.set :refer [union]]))

(defn register-aspect! [dhamma & asp-props]
  (doseq [prop asp-props]
    (s/register-property! (reference dhamma) [:_aspect_of prop])))

(defn remove-aspect! [dhamma & asp-props]
  (doseq [prop asp-props]
    (s/remove-property! (reference dhamma) [:_aspect_of prop])))

(defmulti is-aspect-of? (fn [dhamma _parent] (s/apply-fn :name dhamma)))
(defmethod is-aspect-of? :default [dhamma parent]
  (s/apply-fn (s/apply-fn :is-aspect-of? (value dhamma)) (value parent)))

(defn get-aspects [asp-prop]
  (loop [as asp-prop
         acc []]
    (if (empty? as)
      acc
      (recur (butlast as)
             (union acc (s/with-property [:_aspect_of as]))))))
(defn aspects-of
  "Get aspects of `dhamma`.
  Returns a map of {asp-prop => #{dhamma}}."
  [dhamma]
  (let [asp-props (->> dhamma value :asp-props)
        filter-fn #(is-aspect-of? % dhamma)
        aspects (map
                 #(->> % get-aspects (filter filter-fn))
                 asp-props)]
    (zipmap asp-props aspects)))

(defn pop-asp-stage
  "Get aspects of next stage of dhamma evaluation.
  Returns a tuple of #{asp-dhammas} and a copy of `dhamma` with its first asp-prop removed."
  [dhamma]
  (let [asp-props (->> dhamma value :asp-props)
        aspects (filter #(is-aspect-of? % dhamma)
                        (get-aspects (first asp-props)))
        modified (mutate dhamma
                         #(assoc % :asp-props (rest asp-props)))]
    (list aspects modified)))

(defn has-aspect? [dhamma asp]
  (some #(= asp (take (count asp) %)) (-> dhamma value :asp-props)))
