-- | A DeleteQueue is a queue where entries can be deleted by an
-- IO action.  This is a fairly specialised implementation, designed
-- for event handling.
--
-- Queue entries are either active or invalid.  Once invalid,
-- removeQueue will not return them, but they still take up (a little) memory.
--
-- addQueue, removeQueue, isEmptyQueue, cleanQueue all take a delete queue
-- as argument.  We assume that this argument is not used again.
--
-- Either removeQueue or isEmptyQueue or cleanQueue should be run
-- occasionally, to remove invalid entries.
module Events.DeleteQueue(
   DeleteQueue,
   emptyQueue, -- :: DeleteQueue v
   addQueue, -- :: DeleteQueue v -> v -> IO (DeleteQueue v,IO ())
   -- add an item to the queue, returning the new queue + a new action which
   -- will invalidate that item.
   removeQueue,
   -- :: DeleteQueue v -> IO (Maybe (v,DeleteQueue v,DeleteQueue v))
   -- returns the next active element of the queue, and the succeeding
   -- queue.  3rd result is a queue which is identical to the original
   -- queue.
   isEmptyQueue, -- :: DeleteQueue v -> IO (Maybe (DeleteQueue v))
   -- If queue has active entries, returns it, otherwise return Nothing.
   cleanQueue, -- :: DeleteQueue v -> IO (DeleteQueue v)
   ) where

import Util.Queue
import Events.Cells

newtype DeleteQueue v = DeleteQueue (Queue (Cell v))

emptyQueue :: DeleteQueue v
emptyQueue = DeleteQueue emptyQ

addQueue :: DeleteQueue v -> v -> IO (DeleteQueue v,IO ())
addQueue (DeleteQueue queue) v =
   do
      cell <- newCell v
      let deleteQueue1 = DeleteQueue (insertQ queue cell)
      return (deleteQueue1,emptyCell cell)

cleanQueue :: DeleteQueue v -> IO (DeleteQueue v)
-- cleanQueue pops empty cells from the front of the queue as long as possible
cleanQueue deleteQueue@(DeleteQueue queue) =
   case removeQ queue of
      Nothing -> return deleteQueue
      Just (cell,queue2) ->
         do
            cellContents <- inspectCell cell
            case cellContents of
               Nothing -> cleanQueue (DeleteQueue queue2)
               Just _ -> return (DeleteQueue (insertAtEndQ queue2 cell))


isEmptyQueue :: DeleteQueue v -> IO (Maybe (DeleteQueue v))
-- isEmptyQueue is like cleanQueue, but if the queue is empty returns Nothing.
isEmptyQueue deleteQueue@(DeleteQueue queue) =
   case removeQ queue of
      Nothing -> return Nothing
      Just (cell,queue2) ->
         do
            cellContents <- inspectCell cell
            case cellContents of
               Nothing -> isEmptyQueue (DeleteQueue queue2)
               Just _ ->
                  return (Just (DeleteQueue (insertAtEndQ queue2 cell)))

removeQueue :: DeleteQueue v -> IO (Maybe (v,DeleteQueue v,DeleteQueue v))
removeQueue (DeleteQueue queue) =
   case removeQ queue of
      Nothing -> return Nothing
      Just (cell,queue2) ->
         do
            vOpt <- inspectCell cell
            case vOpt of
               Nothing -> removeQueue (DeleteQueue queue2)
               Just v ->
                  return (Just(v,DeleteQueue queue2,
                     DeleteQueue(insertAtEndQ queue2 cell)))