 (ns battlebots.arena.generation
   (:require [battlebots.constants.arena :refer [arena-key]]
-            [battlebots.arena.utils :refer [get-arena-dimensions update-cell]]))
+            [battlebots.arena.utils :refer [get-arena-dimensions update-cell]]
+            [clojure.tools.logging :as log]))
 
 (defn- get-number-of-items
   "reutrns the number of items based off of a given frequency and the total number

  make sure there are no inaccessible areas"
  [frequency config arena]
  (let [amount (get-number-of-items frequency arena)]
    (place-walls amount arena config)))

(defn- food
  "sprinkle food around the arena and return a new arena"

  [amount item arena]
  (reduce replacer arena (repeat amount item)))

(defn- wrap
  "Wraps out-of-bounds coordinates (zero-based) to opposite edge of m x n arena"
  [[x y] [m n]]
  {:pre [(integer? x) (integer? y) (pos? m) (pos? n)]
   :post [(let [[x y] %] (and (<= 0 x m) (<= 0 y n)))]}
  [(mod x m) (mod y n)])

(def ^:private directions #{:n :s :e :w :ne :se :sw :nw})

(let [moves {:n [ 0 -1] :ne [ 1 -1] :e [ 1  0] :se [ 1  1]
             :s [ 0  1] :sw [-1  1] :w [-1  0] :nw [-1 -1]}]
  (defn- move
    "Return new coordinates for a move from coordinates in direction"
    [coordinates direction [m n :as dimensions] & {:keys [oob] :or {oob :wrap}}]
    {:pre [(moves direction)]
     :post [(let [[x y] %] (and (integer? x) (integer? y)))]}
    (let [oob? (fn [[x y]] (not (and (<= 0 x (dec m)) (<= 0 y (dec n)))))
          coordinates' (mapv + coordinates (moves direction))]
      (if (oob? coordinates')
        (case oob
          :return coordinates
          :wrap (wrap coordinates' dimensions))
        coordinates'))))

(letfn [(block? [arena c] (= (:block arena-key) (get-in arena (reverse c))))]
  (defn- wall-adjacent?
    "Is the given coordinate pair adjacent to a wall?"
    [coordinates arena]
    (let [dimensions (get-arena-dimensions arena)]
      (some (fn [d] (let [c (move coordinates d dimensions)]
                     (when (block? arena c) [d c])))
            (list :n :e :s :w)))))

(defn- place-wall
  "place a single contiguous block wall of max length l starting at coordinates start in direction d"
  [start l d arena]
  {:pre [((complement neg?) l) (#{:n :e :s :w} d)]}
  (let [dimensions (get-arena-dimensions arena)]
    (loop [arena arena l' 0 head start anchor-count 0]
      (let [anchor-count (+ anchor-count (if (wall-adjacent? start arena) 1 0))]
        (if (and (< l' l) (pos-open head arena) (< anchor-count 2))
          (let [arena' (update-cell arena head (:block arena-key))
                head' (move head d dimensions :oob :return)]
            (recur arena' (inc l') head' anchor-count))
          [l' arena])))))

(defn- place-walls
  "places walls in an organized structures around the arena"
  [amount arena]
  (loop [arena arena l (int amount) tries 50]
    (log/debugf "%d tries left to place length %d" tries l)
    (cond
      (and (pos? tries) (pos? l))
      (let [start (find-random-open-space arena)
            direction (rand-nth [:n :s :e :w])
            [l' arena'] (place-wall start l direction arena)]
        (log/debugf "Placed wall of length %d" l')
        (recur arena' (- l l') (dec tries)))
      (pos? tries) arena ; done
      :true (do (log/warn "Can't place walls!")
                arena))))

;; ;;;;;;;;;;;;;;;;;;;;;;;;
;; PLACE ITEMS
