{-# LANGUAGE ExistentialQuantification #-}
-- | A reservoir is the internal storage mechanism for a "Histogram".
-- It provides a generic way to store histogram values in a way that
-- allows us to avoid the need to plumb the implementation type through anything
-- that uses a reservoir.
module Data.Metrics.Reservoir where
import Data.Metrics.Snapshot
import Data.Time.Clock

-- | Encapsulates the internal state of a reservoir implementation.
--
-- The two standard implementations are the ExponentiallyDecayingReservoir and the UniformReservoir.
data Reservoir = forall s. Reservoir
  { reservoirClear :: !(NominalDiffTime -> s -> s)
  -- ^ An operation that resets a reservoir to its initial state
  , reservoirSize :: !(s -> Int)
  -- ^ Retrieve current size of the reservoir.
  -- This may or may not be constant depending on the specific implementation.
  , reservoirSnapshot :: !(s -> Snapshot)
  -- ^ Take snapshot of the current reservoir.
  --
  -- The number of items in the snapshot should always match the reservoir's size.
  , reservoirUpdate :: !(Double -> NominalDiffTime -> s -> s)
  -- ^ Add a new value to the reservoir, potentially evicting old values in the prcoess.
  , reservoirState :: !s
  -- ^ The internal state of the reservoir.
  }

-- | Reset a reservoir to its initial state.
clear :: NominalDiffTime -> Reservoir -> Reservoir
clear t (Reservoir c size ss u st) = Reservoir c size ss u (c t st)
{-# INLINEABLE clear #-}

-- | Get the current number of elements in the reservoir
size :: Reservoir -> Int
size (Reservoir _ size _ _ st) = size st
{-# INLINEABLE size #-}

-- | Get a copy of all elements in the reservoir.
snapshot :: Reservoir -> Snapshot
snapshot (Reservoir _ _ ss _ st) = ss st
{-# INLINEABLE snapshot #-}

-- | Update a reservoir with a new value.
--
-- N.B. for some reservoir types, the latest value is not guaranteed to be retained in the reservoir.
update :: Double -> NominalDiffTime -> Reservoir -> Reservoir
update x t (Reservoir c size ss u st) = Reservoir c size ss u (u x t st)
{-# INLINEABLE update #-}