{-# LANGUAGE Safe #-}
module Network.Tox.DHT.Stamped where

import qualified Data.Foldable    as F
import           Data.List        ((\\))
import           Data.Map         (Map)
import qualified Data.Map         as Map

import           Network.Tox.Time (Timestamp)

{-------------------------------------------------------------------------------
 -
 - :: Implementation.
 -
 ------------------------------------------------------------------------------}

-- | a collection of objects associated with a timestamp.
type Stamped a = Map Timestamp [a]

empty :: Stamped a
empty = Map.empty

-- | add a timestamped object. There is no requirement that the stamp be later
-- than that of previously added objects.
add :: Timestamp -> a -> Stamped a -> Stamped a
add time x = Map.insertWith (++) time [x]

delete :: Eq a => Timestamp -> a -> Stamped a -> Stamped a
delete time x = Map.adjust (\\ [x]) time

findStamps :: (a -> Bool) -> Stamped a -> [Timestamp]
findStamps p = Map.keys . Map.filter (any p)

dropOlder :: Timestamp -> Stamped a -> Stamped a
dropOlder time = Map.mapMaybeWithKey $
  \t x -> if t < time then Nothing else Just x

getList :: Stamped a -> [a]
getList = F.concat

popFirst :: Stamped a -> (Maybe (Timestamp, a), Stamped a)
popFirst stamped =
  case Map.toAscList stamped of
    [] -> (Nothing, stamped)
    assoc:assocs -> case assoc of
      (_, []) -> popFirst $ Map.fromAscList assocs
      (stamp, [a]) -> (Just (stamp, a), Map.fromAscList assocs)
      (stamp, a:as) -> (Just (stamp, a), Map.fromAscList $ (stamp, as):assocs)

{-------------------------------------------------------------------------------
 -
 - :: Tests.
 -
 ------------------------------------------------------------------------------}