{-# LANGUAGE CPP #-}
module Events.HECs (
    HECs(..),
    Event,
    Timestamp,

    eventIndexToTimestamp,
    timestampToEventIndex,
    extractUserMarkers,
    histogram,
    histogramCounts,
  ) where

import Events.EventTree
import Events.SparkTree
import GHC.RTS.Events

import Data.Array
import Data.Text (Text)
import qualified Data.List as L

#if MIN_VERSION_containers(0,5,0)
import qualified Data.IntMap.Strict as IM
#else
import qualified Data.IntMap as IM
#endif

-----------------------------------------------------------------------------

-- all the data from a .eventlog file
data HECs = HECs {
       hecCount         :: Int,
       hecTrees         :: [(DurationTree, EventTree, SparkTree)],
       hecEventArray    :: Array Int Event,
       hecLastEventTime :: Timestamp,
       maxSparkPool     :: Double,
       minXHistogram    :: Int,
       maxXHistogram    :: Int,
       maxYHistogram    :: Timestamp,
       durHistogram     :: [(Timestamp, Int, Timestamp)],
       perfNames        :: IM.IntMap Text
     }

-----------------------------------------------------------------------------

eventIndexToTimestamp :: HECs -> Int -> Timestamp
eventIndexToTimestamp HECs{hecEventArray=arr} n =
  evTime (arr ! n)

timestampToEventIndex :: HECs -> Timestamp -> Int
timestampToEventIndex HECs{hecEventArray=arr} ts =
    search l (r+1)
  where
    (l,r) = bounds arr

    search !l !r
      | (r - l) <= 1 = if ts > evTime (arr!l) then r else l
      | ts < tmid    = search l mid
      | otherwise    = search mid r
      where
        mid  = l + (r - l) `quot` 2
        tmid = evTime (arr!mid)

extractUserMarkers :: HECs -> [(Timestamp, Text)]
extractUserMarkers hecs =
  [ (ts, mark)
  | (Event ts (UserMarker mark) _) <- elems (hecEventArray hecs) ]

-- | Sum durations in the same buckets to form a histogram.
histogram :: [(Int, Timestamp)] -> [(Int, Timestamp)]
histogram durs = IM.toList $ fromListWith' (+) durs

-- | Sum durations and spark counts in the same buckets to form a histogram.
histogramCounts :: [(Int, (Timestamp, Int))] -> [(Int, (Timestamp, Int))]
histogramCounts durs =
  let agg (dur1, count1) (dur2, count2) =
        -- bangs needed to avoid stack overflow
        let !dur = dur1 + dur2
            !count = count1 + count2
        in (dur, count)
  in IM.toList $ fromListWith' agg durs

fromListWith' :: (a -> a -> a) -> [(Int, a)] -> IM.IntMap a
fromListWith' f xs =
    L.foldl' ins IM.empty xs
  where
#if MIN_VERSION_containers(0,5,0)
    ins t (k,x) = IM.insertWith f k x t
#elif MIN_VERSION_containers(0,4,1)
    ins t (k,x) = IM.insertWith' f k x t
#else
    ins t (k,x) =
      let r = IM.insertWith f k x t
          v = r IM.! k
      in v `seq` r
#endif