module PrioritySync.PrioritySync
    (Dispatch.TaskHandle,
     Room.claim,
     Dispatch.dispatch,
     getResult,
     tryGetResult,
     Prioritized.Prioritized(),
     reprioritize,
     Constrained.Constrained(..),
     Unconstrained.Unconstrained(..),
     RoomConstraint.MaxThreads(..),
     Room.Room,
     Room.newRoom,
     Room.userData,
     Room.ClaimMode(..),
     load,
     Occupancy(..),
     Queue.QueueConfigurationRecord(..),
     Queue.fair_queue_configuration,
     Queue.fast_queue_configuration,
     Queue.QueueOrder(..),
     TaskPool.TaskPool,
     TaskPool.schedule,
     TaskPool.newTaskPool,
     TaskPool.simpleTaskPool,
     startQueue,
     stopQueue,
     TaskPool.waitUntilFinished)
    where

import qualified PrioritySync.Internal.Dispatch as Dispatch
import qualified PrioritySync.Internal.Prioritized as Prioritized
import qualified PrioritySync.Internal.Room as Room
import qualified PrioritySync.Internal.RoomConstraint as RoomConstraint
import qualified PrioritySync.Internal.Queue as Queue
import qualified PrioritySync.Internal.TaskPool as TaskPool
import qualified PrioritySync.Internal.Constrained as Constrained
import qualified PrioritySync.Internal.Unconstrained as Unconstrained

import Data.Set as Set
import Control.Concurrent.STM
import Control.Concurrent
import Control.Monad

getResult :: Dispatch.TaskHandle p a -> IO a
getResult task = atomically $ Dispatch.getResult task

tryGetResult :: Dispatch.TaskHandle p a -> IO (Maybe a)
tryGetResult task = atomically $ Dispatch.tryGetResult task

reprioritize :: Dispatch.TaskHandle p a -> (p -> p) -> IO ()
reprioritize task f = atomically $ Prioritized.reprioritize task f

-- | The number of tasks waiting on this 'TaskPool'.
load :: (Ord p) => TaskPool.TaskPool p u -> IO Int
load = atomically . Queue.load . TaskPool.poolQueue

-- | A convenience class to observe the currently running occupants of a 'Room' or 'TaskPool'.
class Occupancy o where
    inUse :: o -> IO (Set ThreadId)
    isEmpty :: o -> IO Bool
    isEmpty = liftM Set.null . inUse

instance (Ord p) => Occupancy (TaskPool.TaskPool p u) where
    inUse pool = atomically $ Room.inUse $ TaskPool.poolRoom pool
    isEmpty pool = atomically $ TaskPool.isEmpty pool

instance Occupancy (Room.Room u) where
    inUse = atomically . Room.inUse
    isEmpty = atomically . Room.isEmpty

startQueue :: TaskPool.TaskPool p a -> IO ()
startQueue = atomically . TaskPool.startQueue

stopQueue :: TaskPool.TaskPool p a -> IO ()
stopQueue = atomically . TaskPool.stopQueue