(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.utils :as utils]
            [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 GL20 GL30]
           [com.badlogic.gdx.graphics.g2d TextureRegion]
           [java.lang Object]
           [com.badlogic.gdx Gdx]))

(def +screen-width+ 320)
(def +screen-height+ 240)

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

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

(defprotocol IInteractable
  (get-script [this location]))

(def default-interaction
  (reify 
    IInteractable
    (get-script [_ [x y]]
      (actions/get-script entities
       (actions/walk-to entities :ego [x y])))))


(defn find-override [screen entities [x y]]
  (first (filter #(and (mouse-in? % [x y])
                       (satisfies? ICursorOverridable %))
                 (get-in entities [:background :interactions]))))

(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])))
        ;; 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 interaction
                     (get-script interaction [x y]))
                   (get-script default-interaction [x y]))]
    
    (script entities)
    entities)) 

(defn flip [anim]
  (animation (animation! anim :get-frame-duration)
             (for [src-frame (animation! anim :get-key-frames)
                   :let [frame (texture (texture! src-frame :get-texture))]]
               (do
                 (texture! frame :set-region src-frame)
                 (texture! frame :flip true false)
                 frame))))

(defn get-ego [screen]
  (let [player-sheet (texture! (texture "player.png") :split 18 36)
        talk-sheet (texture! (texture "ego/talk.png") :split 18 36)
        stand-sheet (texture! (texture "ego/stand.png") :split 18 36)
        walk-right (animation 0.075 (for [i (range 8)]
                                      (texture  (aget player-sheet 0 i))))
        stand-anim (animation 0.1 (for [i (flatten [(repeat 6 [(repeat 10 0) (repeat 3 1) (repeat 20 0)]) 3 4 5 5 5 6 5 6 5 6 5 4 3 ])]
                                    (texture (aget stand-sheet 0 i))))
        talk-anim (animation 0.2 (for [i (range 8)]
                                   (texture  (aget talk-sheet 0 i))))
        ego {:right {:walk walk-right
                     :stand stand-anim
                     :talk talk-anim}
             :left {:walk  (flip walk-right)
                    :stand (flip stand-anim)
                    :talk (flip talk-anim)} 
             
             
             :baseline 95
             :facing :right
             :origin-x 9 
             :origin-y 0 
             :scaled true
             :inventory #{}
             :x 150 :y 95
             :id "ego"}]
    (actions/start-animation screen
                             (merge (animation->texture screen (:stand (:right ego))) ego)
                             :stand)))

(defn update-from-script [screen {{:keys [current started? channel]} :actions :as entities}]
  (if current
    (let [entities (if started? entities (actions/begin current screen entities))
          entities (actions/continue current screen entities)]
      (if (actions/done? current screen entities)
        (let [terminated (actions/terminate current screen entities)]
          (put! (actions/get-channel current) terminated)
          (recur screen (assoc terminated
                          :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)))))

(defn update-cursor [screen {{:keys [current override last]} :cursor :as entities}]
  (when-not (= (or override current)
               last)
    (input! :set-cursor-image (utils/cursor "cursor.png" (or override current)) 0 0))
  (assoc-in entities [:cursor :last] (or override current)))

(defn make-background [& {:keys [collision interactions] :as params}]
  (let [interactions-as-list (for [[id spec] interactions]
                               (reify
                                 IMouseIn
                                 (mouse-in? [_ location]
                                   (apply (apply zone/box (:box spec)) location))
                                 IInteractable
                                 (get-script [_ location]
                                   (:script spec))
                                 ICursorOverridable
                                 (cursor-override [this] (:cursor spec)))
                               )]
    (merge params {:collision (advent.pathfind/map-from-resource collision)
                   :interactions interactions-as-list})))

(defn backgrounds [screen]
  (let [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)))

        peddler-sheet (texture! (texture "outside-castle/peddler-talk.png" ) :split 18 36)
        peddler-talk (animation 0.18 (for [i (flatten [2 3 2 3 2 3 6 1 0 1 0 1 0 1 0 1 2 3 2 3 2 3 6 4 5 4 5 4 5 4 5])]
                                       (aget peddler-sheet 0 i)))
        peddler-stand (animation 0.2 (for [i (flatten [(repeat 5 0) 6])]
                                     (aget peddler-sheet 0 i)))] 
    {:outside-house
     (make-background :interactions
                      {:door {:box [258 100 281 160]
                              :script (actions/get-script
                                       entities
                                       (actions/walk-to entities :ego [262 88])
                                       (actions/talk entities :ego (str "Anyone home?")))}
                       :sword {:box [274 55 305 88]
                               :script (actions/get-script
                                        entities
                                        (actions/talk entities :ego (str "It's the coolest sword I've ever seen!"))
                                        (actions/walk-to entities :ego [290 66])
                                        (actions/talk entities :ego "Maybe I can pull it out."))}
                       :sheep {:box [38 160 71 181]
                               :script (actions/get-script
                                        entities
                                        (if ((get-in @entities [:background :entities :ego :inventory]) :wool)
                                          (actions/talk entities :ego "The sheep has given me enough wool.")
                                          (do (actions/give entities :ego :wool)
                                              (actions/talk entities :ego "I guess his wool is shedding."))))}
                       :right-dir {:box [300 131 320 224]
                                   :script (actions/get-script
                                            entities
                                            (actions/walk-to entities :ego [244 150])
                                            (actions/transition-background entities :behind-house [122 140])
                                            (actions/walk-to entities :ego [172 122]))
                                   :cursor :right}
                       :up-dir {:box [60 180 224 240]
                                :script (actions/get-script
                                         entities
                                         (actions/walk-to entities :ego [137 204]))
                                :cursor :up}
                       :left-dir {:box [0 40 20 140]
                                  :script (actions/get-script
                                           entities
                                           (actions/walk-to entities :ego [0 80])
                                           (actions/transition-background entities :outside-castle [310 80]))
                                  :cursor :left}}
                      :layers [(assoc (texture "bg5.png") :x 0 :y 0 :baseline 0)
                               (assoc (texture "house.png") :x 0 :y 0 :baseline 122)
                               (assoc (texture "overdirt.png") :x 0 :y 0 :baseline 240)
                               (assoc (texture "background-trees.png") :x 0 :y 0 :baseline 44)]
                      :entities {:sheep (actions/start-animation screen
                                                         (assoc (animation->texture screen sheep) :x 38 :y 160 :baseline 160)
                                                         sheep)}
                      :collision "outsidehouse/collision.png"
                      :scale-fn (scaler-fn-with-baseline 110 0.10 1.00))
     :behind-house
     (make-background :interactions
                      {:left-dir {:box [0 131 20 224]
                                   :script (actions/get-script
                                            entities
                                            (actions/walk-to entities :ego [122 140])
                                            (actions/transition-background entities :outside-house [244 150])
                                            (actions/walk-to entities :ego [195 140]))
                                  :cursor :left}
                       :crack {:box [68 100 73 114]
                               :script (actions/get-script
                                        entities
                                        (actions/walk-to entities :ego [70 80])
                                        (actions/talk entities :ego "I can see Fangald, the wizard inside.")
                                        (actions/talk entities :ego "It looks like he's opening his Magi-safe.")
                                        (actions/talk entities :ego "[todo: sounds play.]")
                                        (actions/talk entities :ego "A lot of good it'll do me to know his password while he's still there."))}
                       :mushrooms {:box [247 59 269 76]
                                   :script (actions/get-script
                                            entities
                                            (if ((get-in @entities [:background :entities :ego :inventory]) :mushrooms)
                                              (actions/talk entities :ego "I've already got a junk ton of mushrooms.")
                                              (do
                                                (actions/walk-to entities :ego [242 75])
                                                (actions/give entities :ego :mushrooms)
                                                (actions/talk entities :ego "Perfectly ripe mushrooms!"))))}
                       :window {:box [103 44 130 140]
                                :script (actions/get-script
                                         entities
                                         (actions/walk-to entities :ego [128 100])
                                         (actions/talk entities :ego "I can see Fangald moving around in there but it's hard to see at this angle."))}}
                      :layers [(assoc (texture "behindhouse/background.png") :x 0 :y 0 :baseline 0)
                               (assoc (texture "behindhouse/house.png") :x 0 :y 0 :baseline 122)
                               (assoc (texture "behindhouse/brush.png") :x 0 :y 0 :baseline 240)]
                      :entities {}
                      :collision "behindhouse/collision.png"
                      :scale-fn (scaler-fn-with-baseline 110 0.10 1.00))
     :outside-castle
     (make-background :interactions
                      {:right-dir {:box [300 40 320 140]
                                   :script (actions/get-script
                                            entities
                                            (actions/walk-to entities :ego [310 80])
                                            (actions/transition-background entities :outside-house [0 80]))
                                   :cursor :right}
                       :peddler {:box [110 90 128 146]
                                 :script (actions/get-script
                                          entities
                                          (actions/walk-to entities :ego [191 90])
                                          (actions/talk entities :ego "Hello there, peddler.")
                                          (actions/talk entities :peddler "Good day sir! Care to see any of my wares?" :stop? false)
                                          (actions/talk entities :peddler "I have only the choicest of wares.")
                                          (actions/talk entities :ego "What 'wares' are you selling?")
                                          (actions/talk entities :peddler "I have the choicest of all types of wares..." :stop? false)
                                          (actions/talk entities :peddler "...I'm well stocked on used earplugs..." :stop? false)
                                          (actions/talk entities :peddler "...glass eyes, motivational tapes... " :stop? false)
                                          (actions/talk entities :peddler "...and useful books like this:" :stop? false)
                                          (actions/talk entities :peddler "'Checkers Mastery in Less Than 10 Seconds'")
                                          (actions/talk entities :ego "I sure am interested on that book on checkers.")
                                          (actions/talk entities :peddler "An excellent selection! It is the choicest of checkers book you'll ever find." :stop? false)
                                          (actions/talk entities :peddler "This book will only set you back 75 sheckels.")
                                          (actions/talk entities :ego "But I haven't got any money!")
                                          (actions/talk entities :peddler "Then you won't have the choicest of checkers books."))}}
                      :layers [(assoc (texture "outside-castle/background.png") :x 0 :y 0 :baseline 0)]
                      :entities {:peddler (actions/start-animation screen
                                                                   (assoc (texture "outside-castle/peddler.png") :x 110 :y 90 :baseline 150 :anim nil
                                                                          :talk peddler-talk :stand peddler-stand)
                                                                   :stand)}
                      :collision "outside-castle/collision.png"
                      :scale-fn (scaler-fn-with-baseline 110 0.10 1.00))}))

(defn animate [entity screen]
  (merge entity (animation->texture (update-in screen [:total-time] #(- % (:anim-start entity)))
                                    (:anim entity))))
(defscreen scene
  :on-show
  (fn [screen entities]
    (update! screen :renderer (stage) :camera (orthographic))
    (let [_ (input! :set-cursor-image (utils/cursor "cursor.png" :main) 0 0)
           music (sound "town-music.mp3")
          _ (sound! music :loop 0.20)
          backgrounds (backgrounds screen)]
      {:backgrounds backgrounds
       :actions {:object nil
                 :channel (chan)
                 :current nil
                 :started? false}
       :cursor {:id "cursor"
                :current :main
                :last :main
                :override nil}
       :background (assoc-in  (:outside-house backgrounds)
                              [:entities :ego] (get-ego screen))
       :fps (assoc (label "0" (color :white) ) :x 5 :baseline 9000)}))
  
  :on-render
  (fn [screen [entities]]
    (clear!)
    (.glEnable  (.getGL20 Gdx/graphics) GL20/GL_BLEND)
    #_(.glEnable  (.getGL30 Gdx/graphics) GL30/GL_BLEND)
    (let [entities (update-cursor screen entities)
          entities (update-from-script screen entities)
          entities (update-in entities [:background :entities] (fn [entities]
                                                                 (into entities
                                                                       (for [[id entity] entities]
                                                                         (if (:anim entity)
                                                                           [id (animate entity screen)]
                                                                           [id entity])))))
          all-entities (concat (vals entities) (get-in entities [:background :layers]) (vals (get-in entities [:background :entities])))]
      (label! (:fps entities) :set-text (str (game :fps))) 
      (render! screen (sort-by :baseline all-entities))
      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 (find-override screen entities [x y])]
        (assoc-in entities [:cursor :override] (cursor-override mouse-override))
        (assoc-in entities [:cursor :override] nil))))

  :on-touch-down (fn [screen [entities]]
                   (if (= (button-code :right)
                          (:button screen)) 
                     (assoc-in entities [:cursor :current] :main)
                     (left-click screen entities))))
