(ns sekai.core.entity
  (:require [sekai.core.gamestate
             :as gs
             :refer [game-state]]
            [sekai.core.components :as components]))

(defn set-component! [entity component]
  (swap! game-state update-in [:entities entity]
         assoc (:name component) (:data component)))

(defn get-component [entity component-name]
  (get-in @game-state [:entities entity component-name]))

(defn register-component! [entity comp-spec]
  (if-not (get-component entity (:name comp-spec))
    (set-component! entity comp-spec)
    (throw (ex-info "Component Doubly Registered"
                    {:entity entity :component comp-spec}))))

(defn deregister-component! [entity component]
  (swap! game-state update-in [:entities entity] dissoc component))

(defn update-component! [entity component update-fn]
  (swap! game-state
         (fn [gs]
           (if-let [data (get-in @game-state [:entities entity component])]
             (assoc-in gs [:entities entity component] (update-fn data))
             (throw (ex-info "Unknown Component"
                             {:entity entity :component component}))))))

(defn register-entity! []
  (let [eid (gensym "entity_")]
    (swap! game-state update-in [:entities] assoc eid {})
    (register-component! eid (components/tags))
    eid))

(defn register-tag! [entity tag]
  (swap! game-state update-in [:tags tag] (fnil conj #{}) entity)
  (update-component! entity :tags #((fnil conj #{}) % tag)))

(defn deregister-tag! [entity tag]
  (swap! game-state update-in [:tags tag] disj entity)
  (update-component! entity :tags #(disj % tag)))
