{-# LANGUAGE BangPatterns #-}
module Eventlog.Total (total) where

import Control.Monad.State.Strict (State(), execState, get, put, modify)
import Data.Map (Map, empty, alter)
import Prelude hiding (init, lookup, lines, words, drop, length, readFile)

import Eventlog.Types


data Parse =
  Parse
  { totals    :: !(Map Bucket (Double, Double)) -- compute running totals and total of squares
  , count     :: !Int                         -- number of frames
  }

parse0 :: Parse
parse0 = Parse{ totals = empty, count = 0 }

total :: [Frame] -> (Int, Map Bucket (Double, Double))
total fs =
  let parse1 = flip execState parse0 . mapM_ parseFrame $ fs
  in  (
       count parse1
      , fmap (stddev $ fromIntegral (count parse1)) (totals parse1)
      )

stddev :: Double -> (Double, Double) -> (Double, Double)
stddev s0 (s1, s2) = (s1, sqrt (s0 * s2 - s1 * s1) / s0)


parseFrame :: Frame -> State Parse ()
parseFrame (Frame _time ls) = do
  _ <- mapM inserter ls
  modify $ \p -> p{ count = count p + 1 }

inserter :: Sample -> State Parse Double
inserter (Sample k v) = do
  p <- get
  put $! p { totals = alter (accum  v) k (totals p) }
  return $! v

accum :: Double -> Maybe (Double, Double) -> Maybe (Double, Double)
accum x Nothing  = Just $! (((,) $! x) $! (x * x))
accum x (Just (y, yy)) = Just $! (((,) $! (x + y)) $! (x * x + yy))