{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}

module Hyperion.Analysis
  ( Parameter(..)
  , identifiers
  , analyze
  ) where

import Control.Lens
  ( Contravariant(..)
  , Fold
  , ala
  , foldMapOf
  , folded
  , to
  )

import Control.Lens.Each
import Data.Monoid
import Data.Traversable (for)
import Hyperion.Benchmark
import Hyperion.Internal
import Hyperion.Measurement
import Hyperion.Report

identifiers :: Fold Benchmark BenchmarkId
identifiers = go []
  where
    go :: [Component] -> Fold Benchmark BenchmarkId
    go comps f (Bench name _) = coerce $ f $ BenchmarkId $ comps <> [BenchC name]
    go comps f (Group name bks) = coerce $ (folded.go (comps <> [GroupC name])) f bks
    go comps f (Bracket _ _ g) = go comps f (g Empty)
    go comps f (Series xs g) =
      coerce $ for xs $ \x ->
        go (comps <> [SeriesC (Parameter x)]) f (g x)

    coerce :: (Contravariant f, Applicative f) => f a -> f b
    coerce = contramap (const ()) . fmap (const ())

analyze
  :: BenchmarkId -- ^ Benchmark identifier.
  -> Sample -- ^ Measurements.
  -> Report
analyze ident samp = Report
    { _reportBenchName = renderBenchmarkId ident
    , _reportBenchParams =
        map (\(Parameter x) -> fromEnum x) $ benchmarkParameters ident
    , _reportTimeInNanos =
        totalDuration / trueNumIterations
    , _reportCycles = Nothing
    , _reportAlloc = Nothing
    , _reportGarbageCollections = Nothing
    , _reportMeasurements = Just samp
    }
  where
    totalDuration =
      ala
        Sum
        (foldMapOf (measurements.each.duration.to realToFrac))
        samp
    trueNumIterations =
      ala
        Sum
        (foldMapOf (measurements.each.batchSize.to realToFrac))
        samp