require 'sdl'

class FPSTimerSample
  FPS_COUNT = 10

  attr_accessor :fps
  attr_reader :real_fps, :total_skip
  attr_reader :count_sleep
  # +fps+ is the number of frames per second that you want to keep,
  # +accurary+ is the accurary of sleep/SDL.delay in milisecond
  def initialize(fps = 60, accurary = 10, skip_limit = 15)
    @fps = fps
    @accurary = accurary / 1000.0
    @skip_limit = skip_limit
  end

  # reset timer, you should call just before starting loop
  def reset
    @old = get_ticks
    @skip = 0
    @real_fps = @fps
    @frame_count = 0
    @fps_old = @old
    @count_sleep = 0
    @total_skip = 0
  end

  # execute given block and wait
  def wait_frame
    now = get_ticks
    nxt = @old + (1.0/@fps)
    if nxt > now || @skip > @skip_limit
      yield
      @skip = 0
      wait(nxt)
      @old = nxt
    else
      @skip += 1
      @total_skip += 1
      @old = get_ticks
    end

    calc_real_fps
  end

  private
  def wait(nxt)
    while nxt > get_ticks + @accurary
      sleep(@accurary - 0.005)
      @count_sleep += 1
    end

    while nxt > get_ticks
      # busy loop, do nothing
    end
  end

  def get_ticks
    SDL.get_ticks / 1000.0
  end

  def calc_real_fps
    @frame_count += 1
    if @frame_count >= FPS_COUNT
      @frame_count = 0
      now = get_ticks
      @real_fps = FPS_COUNT / (now - @fps_old)
      @fps_old = now
    end
  end
end

class FPSTimerLight
  N = 12
  DT = 2
  FPS_COUNT = 10
  
  attr_reader :fps
  attr_reader :real_fps
  # +fps+ is the number of frames per second that you want to keep,
  # +accurary+ is the accurary of sleep/SDL.delay in milisecond
  def initialize(fps = 60, accurary = 10, skip_limit = 15)
    @fps = fps
    @accurary = accurary * N
    @skip_limit = 15
    @one_frame = 1000*N / fps
    @delay = accurary - 2
  end
  
  # reset timer, you should call just before starting loop
  def reset
    @old = get_ticks
    @skip = 0
    
    # for calculate real fps
    @frame_count = 0
    @fps_old = @old
    @real_fps = @fps
  end

  def wait_frame
    now = get_ticks
    nxt = @old + @one_frame
    if nxt > now || @skip > @skip_limit
      yield
      @skip = 0
      wait(nxt)
      @old = nxt
    else
      @skip += 1
      @total_skip += 1
      @old = get_ticks
    end

    calc_real_fps
  end

  private
  def get_ticks
    SDL.get_ticks * N
  end
  
  def wait(nxt)
    while nxt > get_ticks + @accurary
      SDL.delay(@delay) 
    end

    while nxt > get_ticks
      # busy loop, do nothing
    end
  end

  def calc_real_fps
    @frame_count += 1
    if @frame_count >= FPS_COUNT
      @frame_count = 0
      now = get_ticks
      @real_fps = (N*1000*FPS_COUNT)/(now - @fps_old)
      @fps_old = now
    end
  end
end
  
if __FILE__ == $0
  timer = FPSTimerSample.new
  log = []
  
  timer.reset
  300.times do
    sleep 0.005 if rand(5) == 0
    timer.wait_frame do
      log << timer.real_fps.to_s
    end
  end
  
  puts log
  printf "skip:%d\n", timer.total_skip
  printf "sleep:%d\n", timer.count_sleep

  puts
  
  timer = FPSTimerLight.new
  log = []
  
  timer.reset
  old = SDL.get_ticks
  300.times do
    sleep 0.005 if rand(5) == 0
    timer.wait_frame do
      log << timer.real_fps
    end
  end

  puts log
end
