(local state (require :state))

; ; ### Use seed to randomize stuff
(love.math.setRandomSeed (state.getSeed))

; Cell Stuff
; The "cell-num" is the number of cells across; it is then squared to make the
; grid. Each cell has a "cell-size" of walls ("wall-width") and hallways
; ("hall-width").
(var map-meta (state.getMap))
(var cell-num (. map-meta :n))
(var (hall-width wall-width) (values (. map-meta :h) (. map-meta :w)))
(var cell-size (+ hall-width wall-width))
; (fn wall-seg [] (+ 10 (math.random 1 3)))
(fn wall-seg [] (love.math.random 11 13))
; Helper functions for dealing with the cell list
(fn cell-row [i] (+ 1 (math.floor (/ (- i 1) cell-num))))
(fn map-index [i] 
	(var cell-col (if (= 0 (% i cell-num)) cell-num (% i cell-num)))
	(+ (* cell-size cell-size cell-num (- (cell-row i) 1))
		 (- (* cell-col cell-size) (- cell-size 1))))

; Meta-Cells is the data before the digits
(var spawn-cell (love.math.random 1 cell-num))
(var leave-cell (love.math.random 
									(+ (* cell-num (- cell-num 1)) 1) (* cell-num cell-num)))
(var spawn-spot 
	{:x cell-size
	 :y (+ (- (* spawn-cell cell-size) (- cell-size 1)) cell-size)})
(fn default-meta-cells []
	(var meta-cells {})
	(for [c 1 (* cell-num cell-num)]
		(table.insert meta-cells {:e false :s false :v false}))
	(tset meta-cells leave-cell :s true)
	meta-cells)

; Cells is the 0's and 1's
(fn convert-cells [meta-cells leave-cell]
	(var cells {})
	(each [k v (ipairs meta-cells)]
		(var cell [])
		(var row-limit (if (. v :e) (+ hall-width wall-width) hall-width))
		(var col-limit (if (. v :s) (+ hall-width wall-width) hall-width))
		(for [i 1 cell-size]
				(for [j 1 cell-size]
						(var cell-index (+ j (* (- i 1) cell-size)))
						(if (and (< i (+ col-limit 1)) (< j (+ row-limit 1)))
								(tset cell cell-index 0)
								(tset cell cell-index (wall-seg)))
						(if (and (> i hall-width) (> j hall-width))
								(tset cell cell-index (wall-seg)))
						(if (= k leave-cell)
								(tset cell cell-index 0))))
		(table.insert cells cell))
	cells)

(fn generate-maze [meta-cells spawn-cell]
; Maze Generation
; 1. Push the starting cell to the stack
; 2. Pop the top of the stack, mark that cell visited
; 3. If there are any unvisited neighbors:
;   a. Push the current cell to the stack
;   b. Choose a random un-visited neighbor
;   c. Adjust walls accordingly
;   d. Push chosen neighbor to the stack
	(var cell-stack [spawn-cell])
	(while (> (length cell-stack) 0)
		(var current-cell (table.remove cell-stack))
		(var next-cells {})
		(tset meta-cells current-cell :v true)

		; Check if any of the current cell's neighbors are viable
			; North - 
			;   if current-cell <= cell-num, then north is a map-side/barrier
			(when (> current-cell cell-num)
					(var n-cell (- current-cell cell-num))
					(if (not (. meta-cells n-cell :v)) (table.insert next-cells {:c n-cell :d 1})))

			; South - 
			;   if current-cell > (- (* cell-num cell-num) cell-num), 
			;       then south is a map-side/barrier
			(when (< current-cell (- (* cell-num cell-num) cell-num))
					(var s-cell (+ current-cell cell-num))
					(if (not (. meta-cells s-cell :v)) (table.insert next-cells {:c s-cell :d 3})))

			; East -
			;   if current-cell % cell-num = 0, then east is a map-side/barrier
			(when (not (= 0 (% current-cell cell-num)))
					(var e-cell (+ current-cell 1))
					(if (not (. meta-cells e-cell :v)) (table.insert next-cells {:c e-cell :d 2})))

			; West -
			;   if current-cell % cell-num = 1, then west is a map-side/barrier
			(when (not (= 1 (% current-cell cell-num)))
					(var w-cell (- current-cell 1))
					(if (not (. meta-cells w-cell :v)) (table.insert next-cells {:c w-cell :d 4})))

		; Randomly choose a viable neighbor, if possible
		(var chosen-next-cell {})
		(when (> (length next-cells) 0)
			(table.insert cell-stack current-cell)
			(set chosen-next-cell (. next-cells (love.math.random 1 (length next-cells))))
			; Adjust walls accordingly
			(case chosen-next-cell
				{:d 1} (tset meta-cells (. chosen-next-cell :c) :s true)
				{:d 2} (tset meta-cells current-cell :e true)
				{:d 3} (tset meta-cells current-cell :s true)
				{:d 4} (tset meta-cells (. chosen-next-cell :c) :e true))
			(table.insert cell-stack (. chosen-next-cell :c)))
	)
	meta-cells
)

(fn generate_cell_map [cells]
	(var cell_map [])
	(for [c 1 (length cells)]
		(var (mi ci) (values (map-index c) 1))
		(for [i 1 cell-size]
			(for [j 1 cell-size]
				(var new-map-index (+ mi (+ (- i 1) (* (- j 1) (* cell-num cell-size)))))
				(var new-cell-index (+ ci (+ (- i 1) (* (- j 1) cell-size)))) 
				(tset cell_map new-map-index (. cells c new-cell-index)))))
	cell_map)


(fn generate-north-spawn [spawn-cell]
	(var (map map-row) (values [] []))
	(for [i 1 cell-size]
		(set map-row [])
		(for [j 1 (- (* (+ 2 cell-num) cell-size) hall-width)] 
			(table.insert map-row (wall-seg)))
		(table.insert map map-row))
	(var spawn (+ (- (* spawn-cell cell-size) (- cell-size 1)) cell-size))
	(tset map cell-size spawn 0)
	(tset map cell-size (+ spawn 1) 22)
	(tset map cell-size (- spawn 1) 22)
	(tset map (- cell-size 1) spawn 33)
	map)

(fn generate-south-exit [cells leave-cell]
	(var (map map-row) (values [] []))
	(for [i 1 (- cell-size hall-width)]
		(set map-row [])
		(for [j 1 (- (* (+ 2 cell-num) cell-size) hall-width)]
			(table.insert map-row (wall-seg)))
		(table.insert map map-row))
	(var escape (+ (* (- leave-cell (* cell-num (- cell-num 1))) cell-size) 1))
	(for [i 1 cell-size]
		(tset map 1 (+ escape (- i 1)) 33))
	map)

(fn generate_map [cells spawn-cell leave-cell]
	(var map [])
	; Generate the northern feature - spawn point
	(each [_ row (ipairs (generate-north-spawn spawn-cell))] (table.insert map row))
	; Pad the east and west sides of the map
	(for [c 1 (* cell-num cell-size)]
		(var map-row [])
		(for [i 1 cell-size] (table.insert map-row (wall-seg)))
		; (table.move cells 
		; 	(+ (* (- c 1) (* cell-num cell-size)) 1)
		; 	(* c (* cell-num cell-size))
		; 	(+ cell-size 1) map-row)
		(for [i (+ (* (- c 1) (* cell-num cell-size)) 1) (* c (* cell-num cell-size))]
			(table.insert map-row (. cells i)))
		(for [i 1 (- cell-size hall-width)] (table.insert map-row (wall-seg)))
		(table.insert map map-row))
	; Generate the northern feature - spawn point
	(each [_ row (ipairs (generate-south-exit cells leave-cell))] (table.insert map row))
	map)

(fn print_cell_map [cells]
	(var output "")
	(for [i 1 (length cells)]
		(set output (.. output " " (. cells i)))
		(if (= 0 (% i (* cell-num cell-size))) (set output (.. output "\n"))))
	(print output))

(fn print_map [map]
	(for [i 1 (length map)]
		(var (r o) (values "" ""))
		(for [j 1 (length (. map i))]
			(set o (if (= 0 (. map i j)) "00" (. map i j)))
			(set r (.. r " " o)))
		(print r)))

(fn generate []
	(var meta-maze (generate-maze (default-meta-cells) spawn-cell))
	(var data-maze (convert-cells meta-maze leave-cell))
	(var data-map (generate_cell_map data-maze))
	(var map (generate_map data-map spawn-cell leave-cell))
	(print_map map)
	(values map spawn-spot))

; (print (.. "SPAWN: " spawn-cell "(" (. spawn-spot :x) "," (. spawn-spot :y) ")"))
; (print (.. "END: " leave-cell))
; (print (.. "Escape: " (- leave-cell (* cell-num (- cell-num 1)))))
{: generate}
