module Data.Metrics.Meter (
  Meter,
  meter,
  mark,
  mark',
  module Data.Metrics.Types
) where
import Control.Lens
import Control.Monad.Primitive
import Data.Primitive.MutVar
import Data.Time.Clock
import Data.Time.Clock.POSIX
import qualified Data.HashMap.Strict as H
import Data.Metrics.Internal
import qualified Data.Metrics.Meter.Internal as P
import qualified Data.Metrics.MovingAverage as A
import qualified Data.Metrics.MovingAverage.ExponentiallyWeighted as EWMA
import Data.Metrics.Types
data Meter m = Meter
  { fromMeter :: !(MV m P.Meter)
  , meterGetSeconds :: !(m NominalDiffTime)
  }
instance PrimMonad m => Rate m (Meter m) where
  oneMinuteRate m = do
    t <- meterGetSeconds m
    updateAndApplyToRef (fromMeter m) (P.tickIfNecessary t) (A.rate . P.oneMinuteAverage)
  fiveMinuteRate m = do
    t <- meterGetSeconds m
    updateAndApplyToRef (fromMeter m) (P.tickIfNecessary t) (A.rate . P.fiveMinuteAverage)
  fifteenMinuteRate m = do
    t <- meterGetSeconds m
    updateAndApplyToRef (fromMeter m) (P.tickIfNecessary t) (A.rate . P.fifteenMinuteAverage)
  meanRate m = do
    t <- meterGetSeconds m
    applyWithRef (fromMeter m) $ P.meanRate t
instance PrimMonad m => Count m (Meter m) where
  count m = readMutVar (fromMeter m) >>= return . view P.count
mark' :: PrimMonad m => Meter m -> Int -> m ()
mark' m x = do
  t <- meterGetSeconds m
  updateRef (fromMeter m) (P.mark t x)
mark :: PrimMonad m => Meter m -> m ()
mark = flip mark' 1
meter :: IO (Meter IO)
meter = mkMeter getPOSIXTime
mkMeter :: PrimMonad m => m NominalDiffTime -> m (Meter m)
mkMeter m = do
  t <- m
  v <- newMutVar $ ewmaMeter t
  return $! Meter v m
ewmaMeter :: NominalDiffTime 
  -> P.Meter
ewmaMeter = P.meterData (EWMA.movingAverage 0.5)