-- title: drop
-- author: pixelbath
-- desc:   99 balls / wallsmash
-- script: lua

-- https://codeincomplete.com/articles/collision-detection-in-breakout/

t=0
dt=0
x=96
y=24

num_rows=10
num_cols=10
rows_start=4

balls={}
lasers=0

blocks={}
blockWidth = 24
blockHeight = 13
previewDotCount = 31

-- starting player stuff
playerBalls = 1
playerLasers = 0
launchBalls = 0
xpos=120
ypos=130
angle=-90
initialAngleSpeed=1
angleMin = -170
angleMax = -10
ballVel = 2.5
laserJitter = 2 -- degrees

-- launch control
ballGapTime = 50
laserGapTime = 20 -- todo: random delay?
launchMode = false
launchBallTimer = 0
launchLaserTimer = 0
launchBalls = playerBalls
launchLasers = playerLasers

-- debug stuff
freeze = false

function generate_grid()
  for row=0,num_rows-1 do
    blocks[row] = {}
    for col=0,num_cols-1 do
      if row < 5 and math.random() > 0.5 then
        blocks[row][col] = 5
      else
        blocks[row][col] = 0
      end
    end
  end
end

function launch_ball()
  theta=math.pi * angle / 180
  table.insert(balls, {
    x = xpos,
    y = ypos,
    vx = ballVel * math.cos(theta),
    vy = ballVel * math.sin(theta),
  })
  launchBalls = launchBalls - 1
end

function draw_preview()
  theta=math.pi * angle / 180
  xtarget=xpos + (160 * math.cos(theta))
  ytarget=ypos + (160 * math.sin(theta))

  dotGapX = (xtarget - xpos) / previewDotCount
  dotGapY = (ytarget - ypos) / previewDotCount
  -- line(xpos, ypos, xtarget, ytarget, 8)

  dotx=xpos
  doty=ypos
  for i=0,previewDotCount do
    pix(dotx, doty, 5)
    dotx = dotx + dotGapX
    doty = doty + dotGapY
  end

  print(playerBalls, xpos-8, 130, 12, false, 1, true)
  print(playerLasers, xpos+5, 130, 10, false, 1, true)
end

function draw_balls_and_lasers()
  for k,ball in pairs(balls) do
    circ(ball.x, ball.y, 1, 12)
    ball.x = ball.x + ball.vx
    ball.y = ball.y + ball.vy

    check_ball_collisions(ball)

    -- wall bounce
    if ball.x <= 0 or ball.x >= 240 then
      ball.vx = ball.vx * -1
    end
    if ball.y <= 0 then
      ball.vy = ball.vy * -1
    end

    -- prune the fallen boyz
    if ball.y >= ypos + 2 then
      table.remove(balls, k)
      if #balls == 0 then
        
      end
    end
  end
end

function circleRect(cx, cy, radius, rx, ry, rw, rh)

  -- temporary variables to set edges for testing
  local testX = cx
  local testY = cy

  -- which edge is closest?
  if cx < rx then         testX = rx          -- test left edge
  elseif cx > rx+rw then  testX = rx+rw end   -- right edge
  if cy < ry then          testY = ry         -- top edge
  elseif cy > ry+rh then  testY = ry+rh end   -- bottom edge

  -- get distance from closest edges
  local distX = cx-testX
  local distY = cy-testY
  local distance = math.sqrt( (distX*distX) + (distY*distY) )

  -- if the distance is less than the radius, collision!
  if distance <= radius then
    return true
  end
  return false
end

function check_ball_collisions(ball)
  for row=0,num_rows-1 do
    for col=0,num_cols-1 do
      blockVal = blocks[row][col]
      if blockVal > 0 then
        local blockX = col * blockWidth
        local blockY = row * blockHeight
        -- if ball.x >= blockX then

        -- end
        -- if ball.x >= col * blockWidth and ball.x <= col * blockWidth + blockWidth then
        --   if ball.y >= row * blockHeight and ball.y <= row * blockHeight + blockHeight then
        --     if row * blockHeight - ball.y <= 1 or row * blockHeight + blockHeight - ball.y <= 1 then
        --       ball.vy = ball.vy * -1
        --       blocks[row][col] = blockVal - 1
        --       freeze = true
        --     else
        --       ball.vx = ball.vx * -1
        --       blocks[row][col] = blockVal - 1
        --       freeze = true
        --     end
        --   end
        -- end

        if circleRect(ball.x, ball.y, 2, blockX, blockY, blockWidth, blockHeight) then
          -- bottom
          if ball.y <= blockY - (blockHeight/2) then
            -- ball.y = blockY+1
            ball.vy = ball.vy * -1
            return
          end
          -- top
          if ball.y >= blockY + (blockHeight/2) then
            -- ball.y = blockY-1
            ball.vy = ball.vy * -1
            return
          end
          -- left
          if ball.x < blockX - (blockWidth/2) then
            -- ball.x = blockX-1
            ball.vx = ball.vx * -1
            return
          end
          -- right
          if ball.x > blockX then
            -- ball.x = blockX+1
            ball.vx = ball.vx * -1
            return
          end
          freeze = true
        end
      end
    end
  end
end

function draw_blocks()
  for row=0,num_rows-1 do
    for col=0,num_cols-1 do
      blockVal = blocks[row][col]
      colorCursor = 1
      printPos = col * blockWidth + 8
      if blockVal > 0 then
        if blockVal < 18 then
          colorCursor = colorCursor + 1
        end
        if blockVal < 14 then
          colorCursor = colorCursor + 1
        end
        if blockVal < 9 then
          colorCursor = colorCursor + 1
          printPos = col * blockWidth + 10
        end

        rect(col * blockWidth, row * blockHeight, blockWidth, blockHeight, colorCursor)
        print(blockVal, printPos, row * blockHeight + 4, 0, false, 1, true)
      end
    end
  end
end

function draw_debug()
  print(angle, 2, 130, 5, false, 1, true)
  print(playerBalls, 18, 130, 5, false, 1, true)
  print(#balls, 24, 130, 5, false, 1, true)
  print(launchMode, 34, 130, 5, false, 1, true)
  print(launchBallTimer, 64, 130, 5, false, 1, true)
end

-- P1 starts at 0; for players up to P4 add 8 to the ID value
-- U D L R, A B X Y -> 0 1 2 3, 4 5 6 7
function handle_input()
  -- move faster
  if btn(5) then
    angleSpeed=initialAngleSpeed + initialAngleSpeed * 0.5
  else
    angleSpeed=initialAngleSpeed
  end

  -- move slower
  if btn(6) then
    angleSpeed=initialAngleSpeed * 0.3
  else
    angleSpeed=initialAngleSpeed
  end

  if btnp(4) then
    launchMode = true
  end

  if btn(2) then
    angle=angle-angleSpeed
  end
  if btn(3) then
    angle=angle+angleSpeed
  end

  -- clamp angle values
  if angle < angleMin then
    angle=angleMin-angleSpeed
  end
  if angle > angleMax then
    angle=angleMax
  end
end

-- run this stuff for init
generate_grid()

function TIC()
  if not freeze then
    t=time()
  end
  cls(0)

  draw_blocks()

  if launchMode then
    if launchBalls > 0 then
      if launchBallTimer == 0 then
        launchBallTimer = ballGapTime
        launch_ball()
      end
      launchBallTimer = launchBallTimer - 1
    end

    if launchLasers > 0 then
      if launchBallTimer == 0 then
        launchLaserTimer = ballGapTime
        launch_laser()
      end
      launchLaserTimer = launchLaserTimer - 1
    end
    draw_balls_and_lasers()

    -- if we're outta balls, turn off launchMode
    if #balls == 0 then
      launchMode = false
      launchBallTimer = 0
      launchLaserTimer = 0

      -- temp
      launchBalls = playerBalls
    end
  else
    handle_input()
    draw_preview()
  end

  draw_debug()
end

-- <TILES>
-- 001:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-- 002:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-- 003:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-- 004:0000000000ccc0000ccccc000ccccc000ccccc0000ccc0000000000000000000
-- </TILES>

-- <WAVES>
-- 000:00000000ffffffff00000000ffffffff
-- 001:0123456789abcdeffedcba9876543210
-- 002:0123456789abcdef0123456789abcdef
-- </WAVES>

-- <SFX>
-- 000:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000304000000000
-- </SFX>

-- <PALETTE>
-- 000:1a1c2c5d275db13e53ef7d57ffcd75a7f07038b76425717929366f3b5dc941a6f673eff7f4f4f494b0c2566c86333c57
-- </PALETTE>

