module Web.Spock.Worker.Queue
    ( WorkerQueue, newQueue, size, enqueue, dequeue, isFull )
where

import Control.Concurrent.STM
import Control.Applicative
import qualified Data.Map.Strict as M
import qualified Data.Vector as V

data WorkerQueue p v
   = WorkerQueue
   { wq_container :: TVar (M.Map p (V.Vector v))
   , wq_maxSize :: Int
   }

newQueue :: Int -> IO (WorkerQueue p v)
newQueue limit =
    (flip WorkerQueue) limit <$> newTVarIO M.empty

size :: WorkerQueue p v -> STM Int
size (WorkerQueue q _) =
    M.size <$> readTVar q

isFull :: WorkerQueue p v -> STM Bool
isFull wq@(WorkerQueue _ sizeLimit) =
    do currSize <- size wq
       return (currSize >= sizeLimit)

enqueue :: Ord p => p -> v -> WorkerQueue p v -> STM ()
enqueue priority value wq@(WorkerQueue q _) =
    do full <- isFull wq
       if full
       then retry
       else modifyTVar' q (M.insertWith (V.++) priority (V.singleton value))

dequeue :: Ord p => WorkerQueue p v -> STM v
dequeue (WorkerQueue q _) =
    do m <- readTVar q
       if M.null m
       then retry
       else runDequeue m
    where
      runDequeue m =
          do let (minPrio, vals) = M.findMin m
             case V.toList vals of
               [workEl] ->
                   do writeTVar q (M.delete minPrio m)
                      return workEl
               (workEl:xs) ->
                   do writeTVar q (M.adjust (const (V.fromList xs)) minPrio m)
                      return workEl
               [] ->
                   error "Library-Error: This should never happen."