content: {-# LANGUAGE TypeApplications #-}
  
  module Data.TimerWheel.Internal.Entries
    ( Entries,
      empty,
      Data.TimerWheel.Internal.Entries.null,
      size,
      insert,
      delete,
      partition,
    )
  where
  
  import Data.Coerce
  import Data.IntPSQ (IntPSQ)
  import qualified Data.IntPSQ as IntPSQ
  import Data.Word (Word64)
  
  newtype Entries
    = Entries (IntPSQ Word64 (IO ()))
  
  -- | An empty collection.
  empty :: Entries
  empty =
    Entries IntPSQ.empty
  {-# INLINEABLE empty #-}
  
  null :: Entries -> Bool
  null =
    coerce (IntPSQ.null @Word64 @(IO ()))
  {-# INLINEABLE null #-}
  
  -- | The number of timers in the collection.
  size :: Entries -> Int
  size =
    coerce (IntPSQ.size @Word64 @(IO ()))
  {-# INLINEABLE size #-}
  
  -- | @insert i n m x@ inserts callback @m@ into collection @x@ with unique
  -- identifier @i@ and "count" @n@. The
  insert :: Int -> Word64 -> IO () -> Entries -> Entries
  insert i n m =
    coerce (IntPSQ.unsafeInsertNew i n m)
  {-# INLINEABLE insert #-}
  
  -- | Delete a timer by id. Returns 'Nothing' if the timer was not found.
  delete :: Int -> Entries -> Maybe Entries
  delete =
    coerce delete_
  {-# INLINEABLE delete #-}
  
  delete_ :: Int -> IntPSQ Word64 (IO ()) -> Maybe (IntPSQ Word64 (IO ()))
  delete_ i xs =
    (\(_, _, ys) -> ys) <$> IntPSQ.deleteView i xs
  
  -- | Extract expired timers.
  partition :: Entries -> ([IO ()], Entries)
  partition (Entries entries) =
    case IntPSQ.atMostView 0 entries of
      (expired, alive) ->
        (map f expired, Entries (IntPSQ.unsafeMapMonotonic g alive))
    where
      f :: (Int, Word64, IO ()) -> IO ()
      f (_, _, m) =
        m
      g :: Int -> Word64 -> IO () -> (Word64, IO ())
      g _ n m =
        (n -1, m)
  {-# INLINEABLE partition #-}
  