{-# LANGUAGE Safe       #-}
{-# LANGUAGE StrictData #-}
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 :: Stamped a
empty = Stamped a
forall k a. Map k a
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 :: Timestamp -> a -> Stamped a -> Stamped a
add Timestamp
time a
x = ([a] -> [a] -> [a]) -> Timestamp -> [a] -> Stamped a -> Stamped a
forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
Map.insertWith [a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
(++) Timestamp
time [a
x]

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

findStamps :: (a -> Bool) -> Stamped a -> [Timestamp]
findStamps :: (a -> Bool) -> Stamped a -> [Timestamp]
findStamps a -> Bool
p = Stamped a -> [Timestamp]
forall k a. Map k a -> [k]
Map.keys (Stamped a -> [Timestamp])
-> (Stamped a -> Stamped a) -> Stamped a -> [Timestamp]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([a] -> Bool) -> Stamped a -> Stamped a
forall a k. (a -> Bool) -> Map k a -> Map k a
Map.filter ((a -> Bool) -> [a] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any a -> Bool
p)

dropOlder :: Timestamp -> Stamped a -> Stamped a
dropOlder :: Timestamp -> Stamped a -> Stamped a
dropOlder Timestamp
time = (Timestamp -> [a] -> Maybe [a]) -> Stamped a -> Stamped a
forall k a b. (k -> a -> Maybe b) -> Map k a -> Map k b
Map.mapMaybeWithKey ((Timestamp -> [a] -> Maybe [a]) -> Stamped a -> Stamped a)
-> (Timestamp -> [a] -> Maybe [a]) -> Stamped a -> Stamped a
forall a b. (a -> b) -> a -> b
$
  \Timestamp
t [a]
x -> if Timestamp
t Timestamp -> Timestamp -> Bool
forall a. Ord a => a -> a -> Bool
< Timestamp
time then Maybe [a]
forall a. Maybe a
Nothing else [a] -> Maybe [a]
forall a. a -> Maybe a
Just [a]
x

getList :: Stamped a -> [a]
getList :: Stamped a -> [a]
getList = Stamped a -> [a]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
F.concat

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

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