--[[

simple scheduler api, allows arbitrary numbers of items to be scheduled at 
different times with only a single system event being queued at once.

--]]


local queue={}
local curTimer=nil
local nextID=1


function getState()
  return queue,curTimer,nextID
end

local function startNextTimer()
  local curTime=os.clock()
  local delay=queue[1].absTime-curTime
  curTimer=os.startTimer(math.max(delay,0))
end

if timer then
  queue,curTimer,nextID=timer.getState()
  --restart last timer, in case we missed it
  if #queue>0 then 
    startNextTimer()
  end
end


local function queueEvent(event)
  for i=1,#queue do
    if queue[i].absTime>event.absTime then
      table.insert(queue,i,event)
      if i==1 then
        startNextTimer()
      end
      return
    end
  end
  queue[#queue+1]=event
  if #queue==1 then
    startNextTimer()
  end
end

--queue an event to occur at some time
function scheduleRepeating(first,interval,count,action,...)
  if interval<0.5 then error("interval must be at least .5s") end
  local args={...}
  local event={absTime=os.clock()+first,action=action,args={...},interval=interval, count=count, id=nextID }
  nextID=nextID+1
  queueEvent(event)
  return event.id
end


function schedule(delay,action,...)
  return scheduleRepeating(delay,1,1,action,...)  
end


function handleTimerEvent(timer)
  if timer==curTimer then
    curTimer=nil
    local event
    local now=os.clock()
    repeat
      event=table.remove(queue,1)
      if type(event.action)=="function" then
        event.action(unpack(event.args))
      elseif type(event.action)=="string" then
        os.queueEvent(event.action,unpack(event.args))
      end
      --if repeating, advance time, decrement counter, and re-queue
      if event.count~=1 then
        if event.count>1 then
          event.count=event.count-1
        end
        event.absTime=event.absTime+event.interval
        queueEvent(event)
      end
    until #queue==0 or queue[1].absTime>now
    if curTimer==nil and #queue>0 then
      startNextTimer()
    end
  end
end

function cancel(id)
  print("canceling timer "..id)
  for i=1,#queue do
    if queue[i].id==id then
      queue[i].action=nil
      break
    end
  end
end


function coTimerRoutine()
  while true do
    local _,timerID=os.pullEventRaw("timer")
    handleTimerEvent(timerID)
  end
end