(ns advent.actions
  (:require [play-clj.core :refer :all]
            [play-clj.ui :refer :all]
            [play-clj.utils :refer :all]
            [play-clj.g2d :refer :all]
            [clojure.pprint]
            [clojure.string :as s]
            [advent.pathfind]
            [advent.actions :as actions]
            [advent.screens.dialogue :as dialogue]
            [clojure.core.async :refer [put! <! <!! >! >!! chan go thread take! alts!!]])
  (:import [com.badlogic.gdx.graphics Pixmap Pixmap$Filter Texture Texture$TextureFilter]
           [com.badlogic.gdx.graphics.g2d TextureRegion]))

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

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


(defn jump-to [screen entities entity [x y]]
  (let [scale-fn (-> entities :background :scale-fn)
        entity (assoc entity :x x
                      :y y
                      :baseline (- 240 y))]
    (if (:scaled entity)
      (assoc entity :scale-x (scale-fn y) :scale-y (scale-fn y))
      entity)))

(defn walk-to [[target-x target-y] target-id]
  (fn [screen entities]
    (let [{from-x :x from-y :y :keys [left right anim] :as target-entity} (entities target-id)]
      (let [delta-x (- target-x from-x)
            delta-y (- target-y from-y)
            mag (Math/sqrt (+ (* delta-x delta-x) (* delta-y delta-y)))
            moved-x (* 1.5 (/ delta-x mag))
            moved-y (* 1.5 (/ delta-y mag))]
        (if (< mag 1)
          (assoc entities target-id (assoc target-entity :actions (rest (:actions target-entity)) :anim nil))
          (assoc entities target-id
                 (assoc (jump-to screen entities target-entity [(+ moved-x from-x) (+ moved-y from-y)])
                   :anim (if (< moved-x 0) left right))))))))

(defn stop [screen entities target-id]
  (update-in entities [target-id] #(merge %
                                          {:anim nil}
                                          (when (:anim %)
                                            (texture (animation! (:anim %) :get-key-frame 0.25))))))


(defn walk-to [entities target-id [x y]]
  (let [c (chan)
        entity (entities target-id)
        path (take-nth 5 (advent.pathfind/visit-all
                          (:collision (:background entities))
                          [(int (:x entity)) (int (:y entity))]
                          [(int x) (int y)]))]
    (doseq [[target-x target-y] path]
      (put! (get-in entities [:actions :channel])
            (reify
              IAction
              (begin [this screen entities]
                (let [{from-x :x from-y :y :keys [left right anim]} (entities target-id)]
                  (let [delta-x (- target-x from-x)]
                    (assoc-in entities [target-id :anim] (if (< delta-x 0) left right)))))

              (continue [this screen entities]
                (let [{from-x :x from-y :y :keys [left right anim] :as target-entity} (entities target-id)]
                  (let [delta-x (- target-x from-x)
                        delta-y (- target-y from-y)
                        mag (Math/sqrt (+ (* delta-x delta-x) (* delta-y delta-y)))
                        moved-x (* 1.5 (/ delta-x mag))
                        moved-y (* 1.5 (/ delta-y mag))]
                    (assoc entities target-id
                           (jump-to screen entities target-entity [(+ moved-x from-x) (+ moved-y from-y)])))))

              (done? [this screen entities]
                (let [{from-x :x from-y :y :keys [left right anim] :as target-entity} (entities target-id)]
                  (let [delta-x (- target-x from-x)
                        delta-y (- target-y from-y)
                        mag (Math/sqrt (+ (* delta-x delta-x) (* delta-y delta-y)))]
                    (< mag 1))))

              (terminate [this screen entities]
                (put! c entities)
                (stop screen entities target-id)))))
    (<!! c)))

(defn get-text-duration [text]
  (* (count (s/split text #" ")) 0.75))

(defn talk [entities target-id text]
  (let [c (chan)
        initial-time (atom nil)]
    (put! (get-in entities [:actions :channel])
          (reify
            IAction
            (begin [this screen entities]
              (let [_ (swap! initial-time #(or % (:total-time screen)))
                    target-y (get-in entities [target-id :y])
                    scale-fn (get-in entities [:background :scale-fn])
                    scale (scale-fn target-y)
                    height (* scale 36)]
                (run! dialogue/talking-screen :on-talk :text text 
                      :x (get-in entities [target-id :x]) :y (+ (get-in entities [target-id :y]) height)
                      :target-id target-id
                      :scale scale)
                (assoc-in entities [target-id :anim] (get-in entities [target-id :talk]))))

            (continue [this screen entities] entities)

            (done? [this screen entities]
              (> (- (:total-time screen)
                    @initial-time)
                 (get-text-duration text)))

            (terminate [this screen entities]
              (put! c entities)
              (run! dialogue/talking-screen :stop-talk :target-id target-id)
              (stop screen entities target-id))))
    (<!! c)))
