(ns advent.action-test)

(defprotocol IAction
  (begin [this state])
  (done? [this state])
  (continue [this state]))


(defmacro do-actions [name & forms]
  `(vector ~@(for [form forms]
               `(fn [~name]
                 ~form))))

(defn walk-to [who & targets ]
  (for [[target-x target-y] targets]
    (fn [state]
     (reify
       IAction
       (begin [this state] (println "Starting Walking") state)
       (continue [this {:keys [x y] :as state}]
         (println "Continue Walking from" x y)
         (Thread/sleep 500)
         (assoc state :x (inc x) :y (inc y)))
       (done? [this {:keys [x y]} ]
         (and (= x target-x)
              (= y target-y)))))))

(defn talk [who text]
  (reify
    IAction
    (begin [this state]
      (println "Speaking:" text)
      (assoc state :time 0))
    (continue [this state]
      (Thread/sleep 200)
      (assoc state :time (inc (:time state))))
    (done? [this {:keys [time]}]
      (< 3 time))))

(defn give-item [item]
  (reify
    IAction
    (begin [this state]
      (println "Receiving item:" item)
      (update-in state [:items] #(conj % item)))
    (continue [this state]
      state)
    (done? [this state]
      true)))


(defn walk-to [who & targets ]
  (for [[target-x target-y] targets]
    (fn [state]
     (reify
       IAction
       (begin [this state] (println "Starting Walking to" target-x target-y) state)
       (continue [this {:keys [x y] :as state}]
         (println "Continue Walking from" x y)
         (Thread/sleep 500)
         (assoc state :x (inc x) :y (inc y)))
       (done? [this {:keys [x y]} ]
         (and (>= x target-x)
              (>= y target-y)))))))

(defn get-script []
  (let [random-loc (+ 3 (rand-int 10))]
    (do-actions state
                (if (= 1 (rand-int 2))
                  (give-item :gold)
                  (give-item :candy))
                (walk-to :ego [3 3] [random-loc random-loc] )
                (if ((:items state) :gold)
                  (talk :ego "I have enough money to buy something")
                  (talk :ego "I'm broke.")))))

(defn test-run []
  (let [state {:x 0 :y 0 :time 0 :items #{}}
        actions (get-script)]
    (loop [actions actions
           state state
           started? false]
      (when (seq actions)
        (let [step ((first actions) state)]
          (if (sequential? step)
            (recur (concat step (rest actions)) state false)
            (let [state (if started?
                          state
                          (begin step state))
                  state (continue step state)]
              (if (done? step state)
                (recur (rest actions) state false)
                (recur actions state true)))))))))
