(ns topdown2d.demoscene
  (:require [topdown2d.collision :as collision]
            [topdown2d.input :as input]
            [topdown2d.sprites :as sprites]
            [topdown2d.dom :refer [by-id]]
            [topdown2d.tileset :as tileset]
            [clojure.walk :as walk]))

(def map-def (atom nil))

(defn init [gamestate scenestate]
  (-> (.fetch js/window "map/map.json")
      (.then #(.json %))
      (.then #(reset! map-def (js->clj %))))
  (merge
   scenestate
   {:tileset {:images {:pokelike (by-id "demoscene-tileset")}
              :def nil}
    :player {:x (- (/ (get-in gamestate [:dimensions :w]) 2) 32)
             :y (- (/ (get-in gamestate [:dimensions :h]) 2) 32)
             :d :s
             :animation {:ids {:s 778 :n 778
                               :e 819 :w 819}
                         :last-cycle 0
                         :curr-id 778
                         :tileset :pokelike}}
    :viewport {:keep-in {:x 0 :y 0
                         :w 2239 :h 2235}
               :tileset :pokelike
               :x 1 :y 1
               :w (get-in gamestate [:dimensions :w])
               :h (get-in gamestate [:dimensions :h])
               ;; pixels per second
               :pps 150}}))

(defn update-player [gamestate player dir]
  (let [old-dir (:d player)
        new-dir (if (= :? dir) old-dir dir)]
    (as-> player p
      (assoc p :d new-dir)
      (if (= :? dir)
        (sprites/reset player)
        (sprites/proc gamestate p)))))

(defn update-viewport [gamestate viewport dir]
  (collision/move-inside
   (assoc viewport :d dir)
   (:keep-in viewport)
   (collision/pps->px gamestate viewport)))

(defn map-tileset-size [tileset]
  (-> tileset
      (assoc :tiles-x (:columns tileset))
      (assoc :tiles-y (/ (:tilecount tileset)
                         (:columns tileset)))))

(defn map-layer-tiles [map-def]
  (let [ftileset (first (:tilesets map-def))]
    (-> map-def
        (update :layers
                #(map (partial tileset/layer->dest ftileset) %)))))

(defn init-map-def [scenestate]
  (if (and (not (get-in scenestate [:tileset :def]))
           (some? @map-def))
    (assoc-in
     scenestate
     [:tileset :def]
     (-> @map-def
         (walk/keywordize-keys)
         (update :tilesets #(map map-tileset-size %))
         map-layer-tiles))
    scenestate))

(defn update-scene [gamestate scenestate]
  (let [player (:player scenestate)
        viewport (:viewport scenestate)
        dir (get-in gamestate [:input :dir])]
    (-> scenestate
        init-map-def
        (assoc :player (update-player gamestate player dir)
               :viewport (update-viewport gamestate viewport dir)))))

(defn draw-scene [gamestate scenestate]
  (let [viewport (:viewport scenestate)
        {:keys [x y w h background]} viewport]
    (when (get-in scenestate [:tileset :def])
      (tileset/draw-viewport (:tileset scenestate)
                             (:ctx gamestate)
                             viewport))
    #_(.drawImage
       (:ctx gamestate)
       (:image viewport)
       x y w h
       0 0 w h))
  #_  (sprites/draw
       (:ctx gamestate)
       (:player scenestate)))
