(ns emptyhead.lib.core
  "Core components of the stdlib."
  (:require [emptyhead.thought.crud :as thought]
            [emptyhead.thought.define :as def]
            [emptyhead.idea.protocol :as prtc]
            [emptyhead.idea.property :as prop]
            [emptyhead.thought.eval :as eval]
            [emptyhead.idea.memtag :as memtag]
            [emptyhead.thought.extend :as extend]
            [emptyhead.lib.io.print]
            [emptyhead.lib.context]
            [emptyhead.lib.compose]))

(def/define! [:emptyhead :core :get-data]
  (fn [thought parent]
    [parent (prtc/val-fn :data parent)]))

(def/define! [:emptyhead :core :return]
  (fn [thought parent]
    [parent (thought/data thought)])
  :constr-fn
  (fn [thought parent]
    (let [[_ data] (thought/pop-stack parent)]
      {:data data})))

(def/define! [:emptyhead :core :nop]
  (fn [thought parent]
    [parent nil]))

(def/define! [:emptyhead :core :add-ext-stage]
  (fn [thought parent]
    (let [[parent stage] (thought/pop-stack parent)
          [_ op] (thought/pop-stack parent)
          exe (prop/just-property op)]
      (thought/add-ext-stage! exe stage)
      [parent nil])))

(def/define! [:emptyhead :core :add-prop]
  (fn [thought parent]
    (let [[down prop] (thought/pop-stack parent)
          [_ op] (thought/pop-stack down)
          exe (prop/just-property op)]
      (prop/register-property! exe prop)
      [parent nil])))

(def/define! [:emptyhead :core :just-prop]
  (fn [thought parent]
    (let [[_ prop] (thought/pop-stack parent)]
      [parent (memtag/uid-of (prop/just-property prop))])))

(def/define! [:emptyhead :core :swap-stack]
  (fn [thought parent]
    (let [[parent first] (thought/pop-stack parent)
          [parent second] (thought/pop-stack parent)]
      [parent first second])))

(def/define! [:emptyhead :core :gobble]
  (fn [thought parent]
    (let [[parent top] (thought/pop-stack parent)
          [parent next] (if (sequential? top) (thought/pop-stack parent) [parent nil])
          out (if (sequential? top) top (list top))
          out (if next (cons next out) out)]
      [parent out])))

(def/define! [:emptyhead :core :execute]
  (fn [thought parent]
    (let [[parent top] (thought/pop-stack parent)
          exe (prop/just-property top)]
      [(eval/execute! exe parent) top])))

(def/define! [:emptyhead :describe :print-stack]
  (fn [thought parent]
    (run! println (reverse (thought/stack parent)))
    [parent nil]))

(def/define! [:emptyhead :core :clear-stack]
  (fn [thought parent]
    [(assoc parent :return []) nil]))

(def/define! [:emptyhead :core :extend]
  (fn [thought parent]
    (let [[down stage] (thought/pop-stack parent)
          [_ op] (thought/pop-stack down)
          exe (prop/just-property op)]
      (extend/register-extension! exe stage)
      [parent nil])))

(def/define! [:emptyhead :core :pop]
  (fn [thought parent]
    (let [[parent _] (thought/pop-stack parent)]
      [parent nil])))

