module Simulation.Aivika.Trans.Queue.Base
       (
        FCFSQueue,
        LCFSQueue,
        SIROQueue,
        PriorityQueue,
        Queue,
        
        newFCFSQueue,
        newLCFSQueue,
        newSIROQueue,
        newPriorityQueue,
        newQueue,
        
        enqueueStrategy,
        enqueueStoringStrategy,
        dequeueStrategy,
        queueNull,
        queueFull,
        queueMaxCount,
        queueCount,
        
        dequeue,
        dequeueWithOutputPriority,
        tryDequeue,
        enqueue,
        enqueueWithInputPriority,
        enqueueWithStoringPriority,
        enqueueWithInputStoringPriorities,
        tryEnqueue,
        tryEnqueueWithStoringPriority,
        queueDelete,
        queueDelete_,
        queueDeleteBy,
        queueDeleteBy_,
        queueContains,
        queueContainsBy,
        clearQueue) where
import Data.Monoid
import Data.Maybe
import Control.Monad
import Control.Monad.Trans
import Simulation.Aivika.Trans.Ref.Base
import Simulation.Aivika.Trans.DES
import Simulation.Aivika.Trans.Internal.Specs
import Simulation.Aivika.Trans.Internal.Parameter
import Simulation.Aivika.Trans.Internal.Simulation
import Simulation.Aivika.Trans.Internal.Dynamics
import Simulation.Aivika.Trans.Internal.Event
import Simulation.Aivika.Trans.Internal.Process
import Simulation.Aivika.Trans.Resource.Base
import Simulation.Aivika.Trans.QueueStrategy
type FCFSQueue m a = Queue m FCFS FCFS FCFS a
type LCFSQueue m a = Queue m FCFS LCFS FCFS a
type SIROQueue m a = Queue m FCFS SIRO FCFS a
type PriorityQueue m a = Queue m FCFS StaticPriorities FCFS a
data Queue m si sm so a =
  Queue { queueMaxCount :: Int,
          
          enqueueStrategy :: si,
          
          enqueueStoringStrategy :: sm,
          
          dequeueStrategy :: so,
          
          enqueueRes :: Resource m si,
          queueStore :: StrategyQueue m sm a,
          dequeueRes :: Resource m so,
          queueCountRef :: Ref m Int
        }
newFCFSQueue :: MonadDES m => Int -> Simulation m (FCFSQueue m a)
newFCFSQueue = newQueue FCFS FCFS FCFS
  
newLCFSQueue :: MonadDES m => Int -> Simulation m (LCFSQueue m a)
newLCFSQueue = newQueue FCFS LCFS FCFS
  
newSIROQueue :: (MonadDES m, QueueStrategy m SIRO) => Int -> Simulation m (SIROQueue m a)
newSIROQueue = newQueue FCFS SIRO FCFS
  
newPriorityQueue :: (MonadDES m, QueueStrategy m StaticPriorities) => Int -> Simulation m (PriorityQueue m a)
newPriorityQueue = newQueue FCFS StaticPriorities FCFS
  
newQueue :: (MonadDES m,
             QueueStrategy m si,
             QueueStrategy m sm,
             QueueStrategy m so) =>
            si
            
            -> sm
            
            -> so
            
            -> Int
            
            -> Simulation m (Queue m si sm so a)
newQueue si sm so count =
  do i  <- newRef 0
     ri <- newResourceWithMaxCount si count (Just count)
     qm <- newStrategyQueue sm
     ro <- newResourceWithMaxCount so 0 (Just count)
     return Queue { queueMaxCount = count,
                    enqueueStrategy = si,
                    enqueueStoringStrategy = sm,
                    dequeueStrategy = so,
                    enqueueRes = ri,
                    queueStore = qm,
                    dequeueRes = ro,
                    queueCountRef = i }
  
queueNull :: MonadDES m => Queue m si sm so a -> Event m Bool
queueNull q =
  Event $ \p ->
  do n <- invokeEvent p $ readRef (queueCountRef q)
     return (n == 0)
  
queueFull :: MonadDES m => Queue m si sm so a -> Event m Bool
queueFull q =
  Event $ \p ->
  do n <- invokeEvent p $ readRef (queueCountRef q)
     return (n == queueMaxCount q)
  
queueCount :: MonadDES m => Queue m si sm so a -> Event m Int
queueCount q =
  Event $ \p -> invokeEvent p $ readRef (queueCountRef q)
dequeue :: (MonadDES m,
            DequeueStrategy m si,
            DequeueStrategy m sm,
            EnqueueStrategy m so)
           => Queue m si sm so a
           
           -> Process m a
           
dequeue q =
  do requestResource (dequeueRes q)
     liftEvent $ dequeueExtract q
  
dequeueWithOutputPriority :: (MonadDES m,
                              DequeueStrategy m si,
                              DequeueStrategy m sm,
                              PriorityQueueStrategy m so po)
                             => Queue m si sm so a
                             
                             -> po
                             
                             -> Process m a
                             
dequeueWithOutputPriority q po =
  do requestResourceWithPriority (dequeueRes q) po
     liftEvent $ dequeueExtract q
  
tryDequeue :: (MonadDES m,
               DequeueStrategy m si,
               DequeueStrategy m sm)
              => Queue m si sm so a
              
              -> Event m (Maybe a)
              
tryDequeue q =
  do x <- tryRequestResourceWithinEvent (dequeueRes q)
     if x 
       then fmap Just $ dequeueExtract q
       else return Nothing
queueDelete :: (MonadDES m,
                Eq a,
                DequeueStrategy m si,
                DeletingQueueStrategy m sm,
                DequeueStrategy m so)
               => Queue m si sm so a
               
               -> a
               
               -> Event m Bool
               
queueDelete q a = fmap isJust $ queueDeleteBy q (== a)
queueDelete_ :: (MonadDES m,
                 Eq a,
                 DequeueStrategy m si,
                 DeletingQueueStrategy m sm,
                 DequeueStrategy m so)
                => Queue m si sm so a
                
                -> a
                
                -> Event m ()
queueDelete_ q a = fmap (const ()) $ queueDeleteBy q (== a)
queueDeleteBy :: (MonadDES m,
                  DequeueStrategy m si,
                  DeletingQueueStrategy m sm,
                  DequeueStrategy m so)
                 => Queue m si sm so a
                 
                 -> (a -> Bool)
                 
                 -> Event m (Maybe a)
queueDeleteBy q pred =
  do x <- tryRequestResourceWithinEvent (dequeueRes q)
     if x
       then do i <- strategyQueueDeleteBy (queueStore q) pred
               case i of
                 Nothing ->
                   do releaseResourceWithinEvent (dequeueRes q)
                      return Nothing
                 Just i ->
                   fmap Just $ dequeuePostExtract q i
       else return Nothing
               
queueDeleteBy_ :: (MonadDES m,
                   DequeueStrategy m si,
                   DeletingQueueStrategy m sm,
                   DequeueStrategy m so)
                  => Queue m si sm so a
                  
                  -> (a -> Bool)
                  
                  -> Event m ()
queueDeleteBy_ q pred = fmap (const ()) $ queueDeleteBy q pred
queueContains :: (MonadDES m,
                  Eq a,
                  DeletingQueueStrategy m sm)
                 => Queue m si sm so a
                 
                 -> a
                 
                 -> Event m Bool
                 
queueContains q a = fmap isJust $ queueContainsBy q (== a)
queueContainsBy :: (MonadDES m,
                    DeletingQueueStrategy m sm)
                   => Queue m si sm so a
                   
                   -> (a -> Bool)
                   
                   -> Event m (Maybe a)
                   
queueContainsBy q pred =
  strategyQueueContainsBy (queueStore q) pred
clearQueue :: (MonadDES m,
               DequeueStrategy m si,
               DequeueStrategy m sm)
              => Queue m si sm so a
              
              -> Event m ()
clearQueue q =
  do x <- tryDequeue q
     case x of
       Nothing -> return ()
       Just a  -> clearQueue q
              
enqueue :: (MonadDES m,
            EnqueueStrategy m si,
            EnqueueStrategy m sm,
            DequeueStrategy m so)
           => Queue m si sm so a
           
           -> a
           
           -> Process m ()
enqueue q a =
  do requestResource (enqueueRes q)
     liftEvent $ enqueueStore q a
     
enqueueWithInputPriority :: (MonadDES m,
                             PriorityQueueStrategy m si pi,
                             EnqueueStrategy m sm,
                             DequeueStrategy m so)
                            => Queue m si sm so a
                            
                            -> pi
                            
                            -> a
                            
                            -> Process m ()
enqueueWithInputPriority q pi a =
  do requestResourceWithPriority (enqueueRes q) pi
     liftEvent $ enqueueStore q a
     
enqueueWithStoringPriority :: (MonadDES m,
                               EnqueueStrategy m si,
                               PriorityQueueStrategy m sm pm,
                               DequeueStrategy m so)
                              => Queue m si sm so a
                              
                              -> pm
                              
                              -> a
                              
                              -> Process m ()
enqueueWithStoringPriority q pm a =
  do requestResource (enqueueRes q)
     liftEvent $ enqueueStoreWithPriority q pm a
     
enqueueWithInputStoringPriorities :: (MonadDES m,
                                      PriorityQueueStrategy m si pi,
                                      PriorityQueueStrategy m sm pm,
                                      DequeueStrategy m so)
                                     => Queue m si sm so a
                                     
                                     -> pi
                                     
                                     -> pm
                                     
                                     -> a
                                     
                                     -> Process m ()
enqueueWithInputStoringPriorities q pi pm a =
  do requestResourceWithPriority (enqueueRes q) pi
     liftEvent $ enqueueStoreWithPriority q pm a
     
tryEnqueue :: (MonadDES m,
               EnqueueStrategy m sm,
               DequeueStrategy m so)
              => Queue m si sm so a
              
              -> a
              
              -> Event m Bool
tryEnqueue q a =
  do x <- tryRequestResourceWithinEvent (enqueueRes q)
     if x 
       then do enqueueStore q a
               return True
       else return False
tryEnqueueWithStoringPriority :: (MonadDES m,
                                  PriorityQueueStrategy m sm pm,
                                  DequeueStrategy m so)
                                 => Queue m si sm so a
                                 
                                 -> pm
                                 
                                 -> a
                                 
                                 -> Event m Bool
tryEnqueueWithStoringPriority q pm a =
  do x <- tryRequestResourceWithinEvent (enqueueRes q)
     if x 
       then do enqueueStoreWithPriority q pm a
               return True
       else return False
enqueueStore :: (MonadDES m,
                 EnqueueStrategy m sm,
                 DequeueStrategy m so)
                => Queue m si sm so a
                
                -> a
                
                -> Event m ()
enqueueStore q a =
  Event $ \p ->
  do invokeEvent p $
       strategyEnqueue (queueStore q) a
     c <- invokeEvent p $
          readRef (queueCountRef q)
     let c' = c + 1
     c' `seq` invokeEvent p $
       writeRef (queueCountRef q) c'
     invokeEvent p $
       releaseResourceWithinEvent (dequeueRes q)
enqueueStoreWithPriority :: (MonadDES m,
                             PriorityQueueStrategy m sm pm,
                             DequeueStrategy m so)
                            => Queue m si sm so a
                            
                            -> pm
                            
                            -> a
                            
                            -> Event m ()
enqueueStoreWithPriority q pm a =
  Event $ \p ->
  do invokeEvent p $
       strategyEnqueueWithPriority (queueStore q) pm a
     c <- invokeEvent p $
          readRef (queueCountRef q)
     let c' = c + 1
     c' `seq` invokeEvent p $
       writeRef (queueCountRef q) c'
     invokeEvent p $
       releaseResourceWithinEvent (dequeueRes q)
dequeueExtract :: (MonadDES m,
                   DequeueStrategy m si,
                   DequeueStrategy m sm)
                  => Queue m si sm so a
                  
                  -> Event m a
                  
dequeueExtract q =
  Event $ \p ->
  do a <- invokeEvent p $
          strategyDequeue (queueStore q)
     invokeEvent p $
       dequeuePostExtract q a
dequeuePostExtract :: (MonadDES m,
                       DequeueStrategy m si,
                       DequeueStrategy m sm)
                      => Queue m si sm so a
                      
                      -> a
                      
                      -> Event m a
                      
dequeuePostExtract q a =
  Event $ \p ->
  do c <- invokeEvent p $
          readRef (queueCountRef q)
     let c' = c  1
     c' `seq` invokeEvent p $
       writeRef (queueCountRef q) c'
     invokeEvent p $
       releaseResourceWithinEvent (enqueueRes q)
     return a