(ns advent.action-test2
  (:require [clojure.core.async :refer [put! <! <!! >! chan go thread take! alts!!]]))

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



(defn talk [action-channel who text]
  (let [c (chan)]
    (put! action-channel
          (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))
            (terminate [this state]
              (put! c state)
              state)))
    (<!! c)))

(defn give-item [action-channel item]
  (let [c (chan)]
    (put! action-channel
     (reify
       IAction
       (begin [this state]
         (println "Receiving item:" item)
         (update-in state [:items] #(conj % item)))
       (continue [this state]
         state)
       (done? [this state]
         true)
       (terminate [this state]
         (put! c state)
         state)))
    (<!! c)))


(defn walk-to [action-channel who & targets ]
  (let [c (chan)]
    (doseq [[target-x target-y] targets]
      (put! action-channel
            (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)))
              (terminate [this state]
                (put! c state)
                state))))
    (<!! c)))

(defmacro get-script [& forms]
  `(fn [action-channel#] (thread ~@forms (put! action-channel# :end))))

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

(defn test-run []
  (let [state {:x 0 :y 0 :time 0 :items #{} :current-action nil}
        action-channel (chan)
        script (run-script state action-channel)]
    (script action-channel)
    (loop [state state
           current-action nil
           started? false]
      (if (= :end current-action)
        nil
        (if current-action
          (let [state (if started? state (begin current-action state))
                state (continue current-action state)]
            (if (done? current-action state)
              (recur (terminate current-action state) nil false)
              (recur state current-action true)))
          (let [[current-action _] (alts!! [action-channel] :default nil)]
            (do (Thread/sleep 50)
                (recur state current-action false))))))))
