(ns sekai.entity.selector
  (:require [sekai.core.gamestate :refer [game-state]]
            [clojure.set :refer [union intersection]]))

(defmulti gather :type)

(defn just [entity] {:type :just :data entity})
(defmethod gather :just [{:keys [data]}]
  #{data})

(defn with-tag [tag] {:type :with-tag :data tag})
(defmethod gather :with-tag [{:keys [data]}]
  (-> @game-state :tags data))

(defn all [] {:type :all :data nil})
(defmethod gather :all [_]
  (-> @game-state :entities keys set))

(defn s-or [& selectors] {:type :or :data selectors})
(defmethod gather :or [{:keys [data]}]
  (->> data (map gather) (reduce union)))

(defn s-and [& selectors] {:type :and :data selectors})
(defmethod gather :and [{:keys [data]}]
  (->> data (map gather) (reduce intersection)))

(defn with-tags [& tags] {:type :all-tags :data tags})
(defmethod gather :all-tags [{:keys [data]}]
  (gather (apply s-and (map with-tag data))))

(defn some-tags [& tags] {:type :some-tags :data tags})
(defmethod gather :some-tags [{:keys [data]}]
  (gather (apply s-or (map with-tag data))))

(defn do-selector [selector fn]
  (run! fn (gather selector)))
