(ns advent.screens.scene
  (:require [play-clj.core :refer :all]
            [play-clj.ui :refer :all]
            [play-clj.utils :refer :all]
            [play-clj.g2d :refer :all]
            [clojure.pprint]
            [advent.pathfind]
            [advent.actions :as actions]
            [advent.zone :as zone]
            [advent.screens.dialogue :refer [talking-screen]]
            [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]
           [java.lang Object]))

(def +screen-width+ 320)
(def +screen-height+ 240)
(def +num-cursors+ 4)

(defprotocol IMouseIn
  (mouse-in? [this location]))

(defprotocol ICursorOverridable
  (cursor-override [this]))

(defprotocol IInteractable
  (interact [this screen entities cursor location]))

(def default-interaction
  (reify 
    IInteractable
    (interact [_ screen entities cursor [x y] ]
      (case cursor
        :walk (actions/get-script
               (actions/walk-to entities :ego [x y])) 
        :look (actions/get-script
               (actions/talk entities 
                             :ego
                             "Looks pretty uninteresting to me.")) 
        :touch (actions/get-script
                (actions/talk entities :ego
                              "Can't do anything with it."))
        :talk (actions/get-script
               (actions/talk entities :ego
                             "It's not much of a conversationalist."))))))


(def +next-cursor+ (into {} (map vector [:walk :touch :look :talk] (drop 1 (cycle [:walk :touch :look :talk])))))
(def +all-cursors+ [:walk :touch :look :talk :right :down :left :up])


(defn cursor [filename which]
  (let [scale 2
        base-cursor (pixmap filename)
        target-width (* 16 scale)
        target-height (* 16 scale)
        resized (Pixmap. target-width target-height (.getFormat base-cursor))
        index (.indexOf +all-cursors+ which)]
    (Pixmap/setFilter Pixmap$Filter/NearestNeighbour)
    (pixmap! resized :draw-pixmap base-cursor (* index 16) 0 16 16
             0 0 target-width target-height)
    resized ))

(defn right-click [screen entities]
  (let [entities (update-in entities [:cursor] #(assoc % :current (+next-cursor+ (:current %))))]
    (input! :set-cursor-image (cursor "cursor.png" (get-in entities [:cursor :current])) 0 0)
    entities))


(defn left-click [screen entities]
  (let [{:keys [x y]} (input->screen screen {:x (:input-x screen) :y (:input-y screen)})
        interaction (first (filter #(mouse-in? % [x y])
                                   (get-in entities [:background :interactions])))
        current-cursor (get-in entities [:cursor :current])
        cursor-override (get-in entities [:cursor :override])
        ;; TODO - hacky way of resetting queue
        entities  (if-let [current-action (get-in entities [:actions :current])]
                    (assoc (actions/terminate current-action screen entities)
                      :actions {:channel (chan) :current nil :started? false})
                    entities)
        script (or (when cursor-override
                     (interact cursor-override screen entities current-cursor [x y]))
                   (when interaction
                     (interact interaction screen entities current-cursor [x y]))
                   (interact default-interaction screen entities current-cursor [x y]))]
    
    (script (get-in entities [:actions :channel]))
    entities)) 

(defn get-ego [screen]
  (let [player-sheet (texture! (texture "player.png") :split 18 36)
        talk-sheet (texture! (texture "ego/talk.png") :split 16 36)
        ego {:right  (animation 0.075 (for [i (range 8)]
                                        (texture  (aget player-sheet 0 i))))
             :left (animation 0.075 (for [i (range 8)]
                                      (texture  (aget player-sheet 1 i))))
             :talk (animation 0.2 (for [i (range 8)]
                                      (texture  (aget talk-sheet 0 i))))
             
             :baseline 95
             :origin-x 9 
             :origin-y 0 
             :scaled true
             :x 150 :y 95
             :id "ego"}]
    (merge (texture (animation! (:right ego) :get-key-frame 0.25)) ego)))

(defn update-from-script [screen {{:keys [current started? channel]} :actions :as entities}]
  (if (= :end current)
    (assoc entities :actions {:channel channel :current nil :started? false})
    (if current
      (let [entities (if started? entities (actions/begin current screen entities))
            entities (actions/continue current screen entities)]
        (if (actions/done? current screen entities)
          (assoc (actions/terminate current screen entities)
            :actions {:channel channel :current nil :started? false})
          (assoc-in entities [:actions :started?] true)))
      (let [[current _] (alts!! [channel] :default nil)]
        (assoc entities :actions {:channel channel :current current :started? false})))))

(defn scaler-fn-with-baseline [baseline minimum-size & [maximum-size]]
  (let [maximum-size (or maximum-size 1.0)]
    (fn [y]
      (if (< y baseline) maximum-size
          (let [percent-complete (- 1.0 (/ (- y baseline) (- +screen-height+ baseline)))
                range (+ (* percent-complete (- maximum-size minimum-size)) minimum-size)]
            range)))))


(defscreen scene
  :on-show
  (fn [screen entities]
    (update! screen :renderer (stage) :camera (orthographic))
    (let [_ (input! :set-cursor-image (cursor "cursor.png" :walk) 0 0)
          sheep-sheet (texture! (texture "outsidehouse/sheep-anim.png") :split 33 21)
          sheep (animation 0.15 (for [i (flatten [(repeat 10 0) 1 2 3 4 5 6 7 4 5 6 7 8 9 10 (repeat 25 11) (repeat 15 12)])]
                                 (aget sheep-sheet 0 i)))
          background (texture "bg5.png")
          background-trees (texture "background-trees.png")
          house (texture "house.png")
          overdirt (texture "overdirt.png")
          music (sound "town-music.mp3")
           ;;_ (sound! music :loop)
          ]
      {
       :actions {:object nil
                 :channel (chan)
                 :current nil
                 :started? false}
       :cursor {:id "cursor" :current :walk }
       :sheep (assoc (animation->texture screen sheep) :x 38 :y 160 :baseline 160 :anim sheep)
       :background (assoc {} 
                     :collision (advent.pathfind/map-from-resource "pathfind-test-big.png")
                     :baseline 0
                     :mouse-overrides [(reify
                                         IMouseIn
                                         (mouse-in? [_ location]
                                           (apply (zone/box 300 131 320 224) location))
                                         IInteractable
                                         (interact [_ _ entities _ _]
                                           (actions/get-script
                                            (actions/walk-to entities :ego [319 160])))
                                         ICursorOverridable
                                         (cursor-override [_] :right))
                                       (reify
                                         IMouseIn
                                         (mouse-in? [_ location]
                                           (apply (zone/box 60 180 224 240) location))
                                         IInteractable
                                         (interact [_ _ entities _ _]
                                           (actions/get-script
                                            (actions/walk-to entities :ego [137 204])))
                                         ICursorOverridable
                                         (cursor-override [_] :up))
                                       (reify
                                         IMouseIn
                                         (mouse-in? [_ location]
                                           (apply (zone/box 0 40 20 140)  location))
                                         IInteractable
                                         (interact [_ _ entities _ _]
                                           (actions/get-script
                                            (actions/walk-to entities :ego [0 80])))
                                         ICursorOverridable
                                         (cursor-override [_] :left))]
                     :interactions [(reify
                                      IMouseIn
                                      (mouse-in? [_ location]
                                        (apply (zone/box 258 100 281 160) location))
                                      IInteractable
                                      (interact [_ _ entities cursor _]
                                        (case cursor
                                          :look
                                          (actions/get-script
                                           (actions/talk entities :ego (str "The last time I went through that door, Fangald turned me into a frog.")))
                                          
                                          :touch
                                          (actions/get-script
                                           (actions/walk-to entities :ego [262 88])
                                           (actions/talk entities :ego (str "Anyone home?"))) 
                                          nil)))
                                    (reify
                                      IMouseIn
                                      (mouse-in? [_ location]
                                        (apply (zone/box 274 55 305 88) location))
                                      IInteractable
                                      (interact [_ screen entities cursor _]
                                        (case cursor
                                          :look
                                          (actions/get-script
                                           (actions/talk entities :ego (str "It's the coolest sword I've ever seen!")))
                                          :touch
                                          (actions/get-script
                                           (actions/walk-to entities :ego [290 66])
                                           (actions/talk entities :ego "Maybe I can pull it out.")) 
                                          nil)))]
                     :scale-fn (scaler-fn-with-baseline 110 0.10 1.00)
                     :layers [(assoc background :x 0 :y 0 :baseline 0)
                              (assoc house :x 0 :y 0 :baseline 122)
                              (assoc overdirt :x 0 :y 0 :baseline 240)
                              (assoc background-trees :x 0 :y 0 :baseline 44)])
       :ego (get-ego screen)
       :fps (assoc (label "0" (color :white) ) :x 5 :baseline 9000)
       }))
  
  :on-render
  (fn [screen [entities]]
    (clear!)
    (let [entities (update-from-script screen entities)
          
          
          _ (label! (:fps entities) :set-text (str (game :fps)))
          entities (if (get-in entities [:sheep :anim])
                     (update-in entities [:sheep] #(merge % (animation->texture screen (:anim %))))
                     entities)
          entities (if (get-in entities [:ego :anim])
                     (update-in entities [:ego] #(merge % (animation->texture screen (:anim %))))
                     entities)]
      (render! screen (sort-by :baseline (concat (vals entities) (get-in entities [:background :layers]))))
      entities))

  :on-resize (fn [screen entities]
               (size! screen 320 240))

  :on-mouse-moved
  (fn [screen [entities]]
    (let [{:keys [x y]} (input->screen screen {:x (:input-x screen) :y (:input-y screen)})]
      (if-let [mouse-override (first (filter #(mouse-in? % [x y]) (get-in entities [:background :mouse-overrides])))]
        (when (not (get-in entities [:cursor :override]))
          (do (input! :set-cursor-image (cursor "cursor.png" (cursor-override mouse-override)) 0 0)
              (assoc-in entities [:cursor :override] mouse-override)))

        (when (get-in entities [:cursor :override])
          (do (input! :set-cursor-image (cursor "cursor.png" (get-in entities [:cursor :current])) 0 0)
              (assoc-in entities [:cursor :override] nil))))))

  :on-touch-down (fn [screen [entities]]
                   (if (= (button-code :right) (:button screen))
                     (right-click screen entities)
                     (left-click screen entities))))
