#!/usr/bin/env clojure
(defn lower_root [n] (int (Math/floor (Math/sqrt n))))

(defn box_size [n] (
                    let
                    [box (lower_root n)]
                         (if (even? box) (dec box) box)))

(defn half_box [n] (int (/ (box_size n) 2)))

(defn sq [n] (* n n))

(defn side_num [n]
  (mod
    (- n (sq (box_size n)))
    (inc (box_size n))))

(defn part1 [n]
  (if (= n 1)
    0
    (+ 1 (half_box n) (Math/abs (- (side_num n) (half_box n) 1)))
    ))

(println "Part 1 including samples")
(println (part1 1))
(println (part1 12))
(println (part1 23))
(println (part1 1024))
(println (part1 361527))

(def directions
  "Infite seq of directions through
  spiral: :right :up :left :left :down, etc."
  (let [dirs (cycle [[:right :up] [:left :down]])
        amount (map inc (range))]
    (mapcat (fn [[d1 d2] amount]
              (concat (repeat amount d1)
                      (repeat amount d2)))
            dirs
            amount)))

(defn next-tile
  "Calculates (n+1)th tile from nth tile"
  [tile direction]
  (let [[axis delta]
        (case direction
          :right [:x 1]
          :left  [:x -1]
          :down  [:y 1]
          :up    [:y -1])]
    (update tile axis + delta)))

(defn sum-of-neighbours
  "Sum of neighbouring tiles"
  [{:keys [x y]}
   tiles]
  (let [neighbour-positions
        (for [dx [-1 0 1]
              dy [-1 0 1]
              :when (not= 0 dx dy)]
          [(+ x dx)
           (+ y dy)])
        neighbours (keep #(get tiles %)
                         neighbour-positions)]
    (reduce +' (map :v neighbours))))

(defn tile-with-bigger-sum
  "Returns first tile with sum > n"
  [n]
  (let [init-tile {:x 0 :y 0 :v 1}]
    (loop [tile init-tile
           tiles {[0 0] init-tile}
           directions directions]
      (let [next-direction (first directions)
            new-tile (next-tile tile next-direction)
            sum (sum-of-neighbours
                  new-tile
                  tiles)
            new-tile (assoc new-tile :v sum)]
        (if (> sum n)
          new-tile
          (recur new-tile
                 (assoc tiles [(:x new-tile)
                               (:y new-tile)]
                        new-tile)
                 (rest directions)))))))

(defn part2 [n]
  (:v (tile-with-bigger-sum n)))

(println "Part 2 including samples")
(println (part2 1))
(println (part2 12))
(println (part2 23))
(println (part2 1024))
(println (part2 361527))
