def tick args
  defaults args
  render args
  calc args
  input args
end

def defaults args
  args.state.ball ||= {
    debounce: 3 * 60,
    size: 10,
    size_half: 5,
    x: 640,
    y: 360,
    dx: 5.randomize(:sign),
    dy: 5.randomize(:sign)
  }

  args.state.paddle ||= {
    w: 10,
    h: 120
  }

  args.state.left_paddle  ||= { y: 360, score: 0 }
  args.state.right_paddle ||= { y: 360, score: 0 }
end

def render args
  render_center_line args
  render_scores args
  render_countdown args
  render_ball args
  render_paddles args
  render_instructions args
end

begin :render_methods
  def render_center_line args
    args.outputs.lines  << [640, 0, 640, 720]
  end

  def render_scores args
    args.outputs.labels << [
      { x: 320,
        y: 650,
        text: args.state.left_paddle.score,
        size_px: 40,
        anchor_x: 0.5,
        anchor_y: 0.5 },
      { x: 960,
        y: 650,
        text: args.state.right_paddle.score,
        size_px: 40,
        anchor_x: 0.5,
        anchor_y: 0.5 }
    ]
  end

  def render_countdown args
    return unless args.state.ball.debounce > 0
    args.outputs.labels << { x: 640,
                             y: 360,
                             text: "%.2f" % args.state.ball.debounce.fdiv(60),
                             size_px: 40,
                             anchor_x: 0.5,
                             anchor_y: 0.5 }
  end

  def render_ball args
    args.outputs.solids << solid_ball(args)
  end

  def render_paddles args
    args.outputs.solids << solid_left_paddle(args)
    args.outputs.solids << solid_right_paddle(args)
  end

  def render_instructions args
    args.outputs.labels << { x: 320,
                             y: 30,
                             text: "W and S keys to move left paddle.",
                             anchor_x: 0.5,
                             anchor_y: 0.5 }
    args.outputs.labels << { x: 920,
                             y: 30,
                             text: "O and L keys to move right paddle.",
                             anchor_x: 0.5,
                             anchor_y: 0.5 }
  end
end

def calc args
  args.state.ball.debounce -= 1 and return if args.state.ball.debounce > 0
  calc_move_ball args
  calc_collision_with_left_paddle args
  calc_collision_with_right_paddle args
  calc_collision_with_walls args
end

begin :calc_methods
  def calc_move_ball args
    args.state.ball.x += args.state.ball.dx
    args.state.ball.y += args.state.ball.dy
  end

  def calc_collision_with_left_paddle args
    if solid_left_paddle(args).intersect_rect? solid_ball(args)
      args.state.ball.dx *= -1
    elsif args.state.ball.x < 0
      args.state.right_paddle.score += 1
      calc_reset_round args
    end
  end

  def calc_collision_with_right_paddle args
    if solid_right_paddle(args).intersect_rect? solid_ball(args)
      args.state.ball.dx *= -1
    elsif args.state.ball.x > 1280
      args.state.left_paddle.score += 1
      calc_reset_round args
    end
  end

  def calc_collision_with_walls args
    if args.state.ball.y + args.state.ball.size_half > 720
      args.state.ball.y = 720 - args.state.ball.size_half
      args.state.ball.dy *= -1
    elsif args.state.ball.y - args.state.ball.size_half < 0
      args.state.ball.y = args.state.ball.size_half
      args.state.ball.dy *= -1
    end
  end

  def calc_reset_round args
    args.state.ball.x = 640
    args.state.ball.y = 360
    args.state.ball.dx = 5.randomize(:sign)
    args.state.ball.dy = 5.randomize(:sign)
    args.state.ball.debounce = 3 * 60
  end
end

def input args
  input_left_paddle args
  input_right_paddle args
end

def input_left_paddle args
  if args.inputs.controller_one.key_down.down  || args.inputs.keyboard.key_down.s
    args.state.left_paddle.y -= 40
  elsif args.inputs.controller_one.key_down.up || args.inputs.keyboard.key_down.w
    args.state.left_paddle.y += 40
  end
end

def input_right_paddle args
  if args.inputs.controller_two.key_down.down  || args.inputs.keyboard.key_down.l
    args.state.right_paddle.y -= 40
  elsif args.inputs.controller_two.key_down.up || args.inputs.keyboard.key_down.o
    args.state.right_paddle.y += 40
  end
end

def solid_ball args
  { x: args.state.ball.x,
    y: args.state.ball.y,
    w: args.state.ball.size,
    h: args.state.ball.size,
    anchor_x: 0.5,
    anchor_y: 0.5 }
end

def solid_left_paddle args
  { x: 0,
    y: args.state.left_paddle.y,
    w: args.state.paddle.w,
    h: args.state.paddle.h,
    anchor_y: 0.5 }
end

def solid_right_paddle args
  { x: 1280 - args.state.paddle.w,
    y: args.state.right_paddle.y,
    w: args.state.paddle.w,
    h: args.state.paddle.h,
    anchor_y: 0.5 }
end