(ns com.oly.static-sites.do-blog.pages.chess-board
  (:require
   [com.oly.static-sites.do-blog.pages.chess.board
    :refer [square-defaults]]
   [com.oly.static-sites.do-blog.pages.chess.movement
    :refer [chess-pos->coords fetch-selected-piece-moves valid-move?]]
   [com.oly.static-sites.do-blog.pages.chess.render
    :refer [create-board enhance-board place-pieces-on-board]]
   [com.oly.static-sites.do-blog.pages.chess.state
    :refer [find-piece game-state start-state translate-char->piece]]
   [com.oly.static-sites.do-blog.pages.chess.vstate :as v]
   [com.oly.static-sites.ui-hiccup.interface :refer [board]]))

;;/addwidget https://blog-test.digitaloctave.com/chess?widgetId=dochess
;;/addwidget https://blog-test.digitaloctave.com/chess?roomI=!YOyzcCyoomnjylcOFP:matrix.org&widgetId=dochess
(def widget (atom {}))

#_(def game-state-viking (r/atom {:board viking-start-state}))


(defn chars->map [text]
  (when (= 0 (mod (count text) 4))
    (loop [positions text
           results []]
      (if (seq positions)
        (recur
         (drop 4 positions)
         (conj results
               (merge (find-piece :white (translate-char->piece (second positions)))
                      {:start-pos (chess-pos->coords (str (nth positions 2) (nth positions 3)))
                       :pos (chess-pos->coords (str (nth positions 2) (nth positions 3)))})))
        results))))


(defn get-square [board-state pos]
  (get @board-state [:board pos] {}))

(defn reset-square
  ([board pos] (reset-square board pos (square-defaults pos)))
  ([board-state pos value]
   (prn "reset")
   (prn pos)
   (swap! board-state assoc-in [:board pos]  value)))

(defn update-square
  ([pos value] (update-square game-state pos value))
  ([board-state pos value]
   (prn (str "update square " pos " " value))
   (swap! board-state update-in [:board pos]
          merge
          value)))

(defn collisions [board-state positions]
  (filterv (fn [[distance x y]]
             (let [piece (get-square board-state [x y])]
               (prn (str "collision = " [x y] " - " (:type piece) " - " (:colour piece)))
               (and (nil? (:type piece))
                    (nil? (:colour piece))))) positions))

(defn highlight-moves
  ([pos] (highlight-moves pos game-state))
  ([pos board-state]
   (->> (fetch-selected-piece-moves
         (first (filter (fn [[k v]] (:selected v)) (-> @board-state :board)))
         (-> @board-state :board))
        (mapv (fn [[distance x y]]
                (update-square
                 board-state
                 [x y]
                 {:highlight
                  (case (+ 2 distance)
                    2 (str " bg-green-200 ")
                    3 (str " bg-green-300 ")
                    4 (str " bg-green-400 ")
                    5 (str " bg-green-500 ")
                    6 (str " bg-green-600 ")
                    7 (str " bg-green-700 ")
                    8 (str " bg-green-800 "))
                  :pos [x y]}))))))

(defn clear-keys
  ([key] (clear-keys game-state key))
  ([board-state key]
   (swap! board-state
          assoc :board
          (reduce-kv (fn [m pos piece]
                       (assoc m pos (dissoc piece key))) {}
                     (-> @board-state :board)))))

(defn get-selected-square [board-state]
  (last (filter (fn [[_ square]] (true? (:selected square))) (-> @board-state :board))))

(defn piece-attribute-match?
  [src-piece dest-piece attrib]
  (prn (str "attrib " attrib " " (get src-piece attrib) " - " (get dest-piece attrib)))
  (if (and (:type dest-piece) (= (get src-piece attrib) (get dest-piece attrib)))
    true false))

(defn take-piece?
  "Check we actually have a piece and not an empty square
  Also check the colours do not match"
  [piece-one piece-two]
  (if (and (:type piece-two)
           (not= (:colour piece-one) (:colour piece-two)))
    true false))

(defn switch-player []
  (if (= (-> @game-state :player-one :selected) true)
    (do (swap! game-state assoc-in [:player-one :selected] false)
        (swap! game-state assoc-in [:player-two :selected] true))
    (do (swap! game-state assoc-in [:player-one :selected] true)
        (swap! game-state assoc-in [:player-two :selected] false))))

(defn current-player []
  (if (= (-> @game-state :player-one :selected) true)
    (-> @game-state :player-one)
    (-> @game-state :player-two)))

(defn move-piece
  ([selected-piece dest-square] (move-piece game-state selected-piece dest-square))
  ([board-state selected-piece dest-square]
   ;; check the colours don't match can't land on our own piece
   (if (piece-attribute-match? selected-piece dest-square :colour)
     nil
     (when
      (valid-move? (:pos dest-square) (-> @board-state :board))
       (switch-player)
       (when (take-piece? selected-piece dest-square)
         (prn (str "taking " dest-square))
         (prn (str "taking " (:colour dest-square)))
         (swap! board-state update (if (= :white (:colour selected-piece)) :player-one :player-two) conj dest-square))

       (prn "#####")
       (update-square
        (:pos dest-square)
        (merge dest-square {:piece (assoc selected-piece :pos (:pos dest-square))}))
       (reset-square board-state
                     (:pos selected-piece)
                     (square-defaults (:pos selected-piece)))))))

(defn click-square
  ([evt p] (click-square game-state evt  p))
  ([board-state evt p]
   (let [[selected-pos {selected-piece :piece}] (get-selected-square board-state)
         clicked-square p]
     (prn "click square" p)
     (clear-keys board-state :highlight)
     (when (piece-attribute-match? (current-player) selected-piece :colour)
       (when selected-pos
         (move-piece board-state selected-piece clicked-square)))

     (clear-keys board-state :selected)
     (clear-keys board-state :highlight)
     ;; set new square as selected
     (update-square board-state (:pos p) (assoc (get-square board-state (:pos p)) :selected true))
     (highlight-moves (:pos p) board-state))))

(defonce viking-chess-board-setup
  (reset! v/game-state
          {:player-one {:pieces []
                        :colour :white
                        :selected true}
           :player-two {:pieces []
                        :colour :black
                        :selected false}
           :board
           (-> (create-board 11 square-defaults)
               (enhance-board (partial click-square @game-state))
               (place-pieces-on-board v/viking-start-state (partial click-square @game-state)))}))

(defn chess-board-setup [start-state]
  (reset! game-state
          {:player-one {:pieces []
                        :colour :white
                        :selected true}
           :player-two {:pieces []
                        :colour :black
                        :selected false}
           :board
           (-> (create-board 8 square-defaults)
               (enhance-board click-square)
               (place-pieces-on-board start-state click-square))}))

(defn chess-page [{:keys [path-params query-params] :as route}]
  (if (:pieces query-params)
    (chess-board-setup (chars->map (:pieces query-params)))
    (chess-board-setup start-state))
  (fn [{:keys [path-params] :as route}]
    [:div
     (let [[pos piece] (get-selected-square game-state)]
       [:div
        [:div (str @widget)]
        [:div (if (-> @game-state :player-one :selected)
                "Whites turn"
                "Black turn")]
        [:div "Player one " (str (mapv :type (-> @game-state :player-one :pieces)))]
        [:div "Player two " (str (mapv :type (-> @game-state :player-two :pieces)))]
        [:div (str "pos = " pos)]
        [:div (str (:type piece))]
        [:div (str piece)]
        [:div (str "move = " (fetch-selected-piece-moves
                              [pos piece]
                              (-> @game-state :board)))]])

     [:div (board 8 (-> @game-state :board))]
     [:div "Viking chess board"]
     [:div (board 11 (-> @v/game-state :board))]]))

(comment
  (board-test 5)
  (board 5 (-> @game-state :board))
  (get-selected-square game-state)
  @game-state)


