(local overlay (require :overlay))
(local state (require :state))
(local mapper (require :mapper))

(love.graphics.setNewFont 10)
(local pi math.pi)

(var map (mapper.generate 10 10))

; This sets the wall texture data
(var walls [])
(var wall-textures (love.filesystem.getDirectoryItems "textures/walls"))

(each [_ v (ipairs wall-textures)]
  (local wall {})
  (tset wall :t (love.graphics.newImage (.. "textures/walls/" v)))
  (tset wall :w (love.graphics.getWidth (. wall :t)))
  (tset wall :h (love.graphics.getHeight (. wall :t)))
  (table.insert walls wall))

; Map size
(var map-height (length map))
(var map-width (length (. map 1)))

; Player position and direction
(var player (state.getPlayer))

; Screen size
(var (screen-width screen-height) (love.window.getMode))
; (var screen-width 1280)
; (var screen-height 720)
(var texel-width 64)
(var texel-height 64)

; Ray-casting function
(fn cast-ray [ray-angle]
  (local dx (math.cos ray-angle))
  (local dy (math.sin ray-angle))
  (var rays [])
  
  (var (distance tex done hit-pos) (values 0 1 false {}))
  (while (not done)
    (var ray-x (+ player.x (* dx distance)))
    (var ray-y (+ player.y (* dy distance)))
    (set rays [ray-x ray-y])
    
    ; Check if ray hits a wall (>= 1) on the map
    (if (or (>= (math.floor ray-x) map-width)
              (>= (math.floor ray-y) map-height)
              (<= ray-x 0)
              (<= ray-y 0)
              (>= (. map (math.floor ray-y) (math.floor ray-x)) 1))
      (do 
        (set done true)
        (set tex (. map (math.floor ray-y) (math.floor ray-x)))
        (set hit-pos {:x (- ray-x (math.floor ray-x)) :y (- ray-y (math.floor ray-y))}))
      ; Increment distance
      (set distance (+ distance 0.01))))

  (values distance hit-pos tex rays))

; Function to handle player movement
(fn move-player [move-speed]
  (let [new-x (+ player.x (* (math.cos player.d) move-speed))
        new-y (+ player.y (* (math.sin player.d) move-speed))]
    ; Check for collisions with walls
    (when (= (. map (math.floor new-y) (math.floor new-x)) 0)
      (state.modO -1) (state.setX new-x) (state.setY new-y))))

; Draw function for rendering
{:draw (fn love.draw []
  (love.graphics.clear)
  
  ; For each vertical slice of the screen
  (var (last-x last-y) (values 0 0))
  (for [i 0 (- screen-width 1)]
    ; Calculate angle of ray relative to player direction
    (local ray-angle (+ player.d (- (* (/ i screen-width) player.f) (/ player.f 2))))
    
    ; Cast the ray to find distance to the nearest wall
    (local (distance hit-pos tex rays) (values (cast-ray ray-angle)))
    
    ; Calculate height of the wall slice
    (local wall-height (math.floor (/ screen-height distance)))
    (local start-y (/ (- screen-height wall-height) 2))
    (local end-y (/ (+ screen-height wall-height) 2))

    ; Draw a textured wall
    (local wall-texture (. walls tex))
    (var texture-x 0)
    (if (> (/ (- (. rays 1) last-x) (- (. rays 2) last-y)) (/ (- (. rays 2) last-y) (- (. rays 1) last-x)))
      (set texture-x (math.floor (* (. hit-pos :y) (. wall-texture :w))))
      (set texture-x (math.floor (* (. hit-pos :x) (. wall-texture :w)))))
    (love.graphics.draw (. wall-texture :t)
                        (love.graphics.newQuad texture-x 0 1 (. wall-texture :h) (. wall-texture :w) (. wall-texture :h))
                        i start-y 0 1 (/ wall-height (. wall-texture :h)))
    (when (= 0 (% i 200))
      (love.graphics.print (.. "r: " ray-angle) i 10)
      (love.graphics.print (.. "h-x: " (. rays 1)) i 20)
      (love.graphics.print (.. "h-y: " (. rays 2)) i 30)
    )
    (set (last-x last-y) (values (. rays 1) (. rays 2)))
    )

  (overlay.overlay)
  (set player (state.getPlayer))
  )

 :keypressed (fn keypressed [key set-mode]
      (when (= key "j") (state.setD (- player.d (/ pi 2))))
      (when (= key "l") (state.setD (+ player.d (/ pi 2))))
      (when (= key "i") (move-player 1))
      (when (= key "k") (move-player -1))
      (when (= key "x") (love.event.quit)))}
