(ns com.oly.static-sites.do-blog.pages.chess.movement
  (:require
   [cljs.test :refer [deftest]]))

(defn get-square [pos game-state]
  (get game-state pos {}))

(defn chess-pos->coords [chars]
  [( + 1 (first (keep-indexed #(when (= (first chars) %2) %1) [\A \B "C" \D \E \F \G \H])))
  (js/parseInt (second chars))])

(defn inside-grid-edge?
  "Return true if x and y are inside the grid"
  [size x y]
  (let [board-size (+ 1 size)]
    (and (and (< x board-size) (< y board-size))
         (and (> x -1) (> y -1)))))

(defn outside-grid-edge?
  "Return true if x and y are outside the grid"
  [size x y]
  (not (inside-grid-edge? size x y)))

(defn edge?
  "Return true if x and y are inside the grid"
  [[distance x y]]
  (if (or (nil? distance) (= distance 1))
    true
    (and (and (< x 9) (< y 9))
         (and (> x 0) (> y 0)))))

(defn opposite-piece?
  "Check the board for a piece, and check its a different colour"
  [src-pos dest-pos board]
  (and (-> (get board dest-pos) :piece :type some?)
       (not= (-> (get board src-pos) :piece :colour)
             (-> (get board dest-pos) :piece :colour))))

(defn piece-at-destination?
  "Is the destination spot a piece or empty square ?"
  [src-pos dest-pos board]
  (-> (get board dest-pos) :piece :type some?))

(defn end-seq?
  "Check if its an empty sequence or the sequence has ended with a nil"
  [s]
  (if (empty? s)
    false
    (nil? (last s))))

(defn identity-step [value _] value)

(defn line-step
  "Given if the move to the next position is valid"
  [board size step x1 y1 x2 y2 cords]
  (if (outside-grid-edge? size x2 y2) #_(and 
                                         (piece-at-destination? [x1 y1] [x2 y2] board))
    (do  (conj cords nil))
    (if (opposite-piece? [x1 y1] [x2 y2] board)
      (do (conj cords nil))
      (do (conj cords [step x2 y2]))
      )))


(defn line-move
  "Use the same function for horizontal vertical and diagonals
   We can abstract this because all that change's is which axes we calculate on
  Just adjust the x1-fn x2-fn y1-fn y2-fn fn's"
  [[x y] size x1-fn y1-fn x2-fn y2-fn board]
  (loop [step 1
         cords-set-one []
         cords-set-two []]
    ;; nil at end of list indicates end of valid moves
    (prn (concat
             cords-set-one
             cords-set-two))
    (if (and (end-seq? cords-set-one)
             (end-seq? cords-set-two))
      (concat
       (drop-last cords-set-one)
       (drop-last cords-set-two))
      (recur (+ step 1)
             (if (end-seq? cords-set-one)
               cords-set-one
               (line-step board size step x y (x1-fn x step) (y1-fn x step) cords-set-one))
             (if (end-seq? cords-set-two)
               cords-set-two
               (line-step board size step x y (x2-fn x step) (y2-fn x step) cords-set-two)
               )))))

(defn line-horizontal [[x y] size board]
  (prn "horizontal")
  (line-move [x y] size + identity-step - identity-step board))

(defn line-vertical [[x y] size board]
  (line-move [x y] size identity-step + identity-step - board))

(defn diagonal-ascending
  "down and to the right"
  [[x y] size board] (line-move [x y] size + + - - board))

(defn diagonal-descending
  "down and to the left"
  [[x y] size board] (line-move [x y] size + - - + board))

(defn line-projection [[x y] size board]
  (concat (drop-last (line-horizontal [x y] size board))
          (line-vertical [x y] size board)))

(defn diagonal-projection [[x y] size board]
  (concat (diagonal-ascending [x y] size board)
          (diagonal-descending [x y] size board)))

(defn directional-projection [[x y] size board]
  (concat (diagonal-projection [x y] size board)
          (line-projection [x y] size board)))

(defn star [[x y] size]
  (loop [step 1
         cords [[0 x y]]]
    (case step
      3
      (recur (+ step 1)
             (conj cords
                   ;[(- (-> cords first second) s) (-> cords first last)]
                   [step (- (-> cords first second) 2)  (+ (-> cords first last) 1)]
                   [step (- (-> cords first second) 2)  (- (-> cords first last) 1)]

                   ;[(+ (-> cords first second) s) (-> cords first last)]
                   [step (+ (-> cords first second) 2)  (- (-> cords first last) 1)]
                   [step (+ (-> cords first second) 2)  (+ (-> cords first last) 1)]

                   [step (+ (-> cords first second) 1) (- (-> cords first last) 2)]
                   [step (- (-> cords first second) 1) (- (-> cords first last) 2)]

                   [step (- (-> cords first second) 1) (+ (-> cords first last) 2)]
                   [step (+ (-> cords first second) 1) (+ (-> cords first last) 2)]))
      4 cords
      (recur (+ step 1)
             cords
             #_(conj cords
                     [step (- (-> cords first second) s) (-> cords first last)]
                     [(+ (-> cords first second) s) (-> cords first last)]
                     [(-> cords first second)  (- (-> cords first last) s)]
                     [(-> cords first second)  (+ (-> cords first last) s)])))))

(defn all-around-projection [[x y] size]
  [[0 (- x 1) (+ 1 y)] [0 x (+ 1 y)] [0 (+ 1 x) (+ 1 y)]
   [0 (- x 1) y] [0 (+ 1 x) y]
   [0 (- x 1) (- y 1)] [0 x (- y 1)] [0 (+ 1 x) (- y 1)]])

(defn pawn-projection [[x y] direction size board]
  (concat [(when  (opposite-piece? [x y] [(+ x 1) (+ y direction)] board)
             [0 (+ x 1) (+ y direction)])
           (when  (opposite-piece? [x y] [(- x 1) (+ y direction)] board)
             [0 (- x 1) (+ y direction)])]
          (if (or (= y 2) (= y 7))
            [[0 x (+ y direction)] [1 x (+ y (* 2 direction))]]
            [[0 x (+ y direction)]])))


(defn fetch-selected-piece-moves [[pos {:keys [piece]}] game-state]
  (case (:type piece)
    :king (all-around-projection pos #_(:pos piece) 8)
    :queen (directional-projection pos #_(:pos piece) 8 game-state)
    :rook (line-projection pos #_(:pos piece) 8 game-state)
    :knight (star pos #_(:pos piece) 8)
    :pawn (pawn-projection pos #_(:pos piece) (if (= :white (:colour piece)) 1 -1) 8 game-state)
    :bishop (diagonal-projection pos #_(:pos piece) 8 game-state)
    (prn (str "type = " (:type piece)))))

(defn valid-move? [dest-pos game-state]
  (let [piece (first (filter (fn [[k v]] (:selected v)) game-state))
        moves (fetch-selected-piece-moves piece game-state)]
    ;; if selected and destintion pos are the same no need to move
    (when (not= dest-pos (:pos piece))
      (some (fn [[_distance x y]] (= [x y] dest-pos)) moves))))

;; (line-horizontal [8 1] 8)
;; (line-vertical [8 1] 8)

;; ;(line-projection [8 1] 8)
;; (directional-projection [1 1] 8)
;; (directional-projection [8 1] 8)
;; (star [8 1] 8)
;; (directional-projection [5 1] 8)

;; (deftest line-projection-test
;;   (testing "Horizontal direction"
;;     (is (= (line-horizontal [2 3] 8)
;;            [[1 3] [2 3] [3 3] [4 3] [5 3] [6 3] [7 3] [8 3]])))
;;   (testing "Vertical direction"
;;     (is (= (line-vertical [2 3] 8)
;;            [[2 1] [2 2] [2 3] [2 4] [2 5] [2 6] [2 7] [2 8]]))))

;; (deftest diagonal-projection-test
;;   (testing "Diagonal ascending direction"
;;     (is (= (diagonal-ascending [2 3] 8)
;;            [[1 2] [2 3] [3 4] [4 5] [5 6] [6 7] [7 8]])))
;;   (testing "Diagonal descending direction"
;;     (is (= (diagonal-descending [2 3] 8)
;;            [[8 7] [7 6] [6 5] [5 4] [4 3] [3 2] [2 1]])))

;;   (testing "Diagonals "
;;     (is (= (diagonal-projection [2 3] 8)
;;            [[1 2] [2 3] [3 4] [4 5] [5 6] [6 7] [7 8]]))))
