module GHC.Core.LateCC.Utils
  ( -- * Inserting cost centres
    doLateCostCenters -- Might be useful for API users

    -- ** Helpers for defining insertion methods
  , getCCFlavour
  , insertCC
  ) where

import GHC.Prelude

import Control.Monad
import Control.Monad.Trans.Class
import Control.Monad.Trans.Reader
import Control.Monad.Trans.State.Strict
import qualified Data.Set as S

import GHC.Core
import GHC.Core.LateCC.Types
import GHC.Core.Utils
import GHC.Data.FastString
import GHC.Types.CostCentre
import GHC.Types.CostCentre.State
import GHC.Types.SrcLoc
import GHC.Types.Tickish

-- | Insert cost centres into the 'CoreProgram' using the provided environment,
-- initial state, and insertion method.
doLateCostCenters
  :: LateCCEnv
  -- ^ Environment to run the insertion in
  -> LateCCState s
  -- ^ Initial state to run the insertion with
  -> (CoreBind -> LateCCM s CoreBind)
  -- ^ Insertion method
  -> CoreProgram
  -- ^ Bindings to consider
  -> (CoreProgram, LateCCState s)
doLateCostCenters env state method binds =
    runLateCC env state $ mapM method binds

-- | Evaluate late cost centre insertion
runLateCC :: LateCCEnv -> LateCCState s -> LateCCM s a -> (a, LateCCState s)
runLateCC env state = (`runState` state) . (`runReaderT` env)

-- | Given the name of a cost centre, get its flavour
getCCFlavour :: FastString -> LateCCM s CCFlavour
getCCFlavour name = mkLateCCFlavour <$> getCCIndex' name
  where
    getCCIndex' :: FastString -> LateCCM s CostCentreIndex
    getCCIndex' name = do
      cc_state <- lift $ gets lateCCState_ccState
      let (index, cc_state') = getCCIndex name cc_state
      lift . modify $ \s -> s { lateCCState_ccState = cc_state'}
      return index

-- | Insert a cost centre with the specified name and source span on the given
-- expression. The inserted cost centre will be appropriately tracked in the
-- late cost centre state.
insertCC
  :: FastString
  -- ^ Name of the cost centre to insert
  -> SrcSpan
  -- ^ Source location to associate with the cost centre
  -> CoreExpr
  -- ^ Expression to wrap in the cost centre
  -> LateCCM s CoreExpr
insertCC cc_name cc_loc expr = do
    cc_flavour <- getCCFlavour cc_name
    env <- ask
    let
      cc_mod = lateCCEnv_module env
      cc = NormalCC cc_flavour cc_name cc_mod cc_loc
      note = ProfNote cc (lateCCEnv_countEntries env) True
    when (lateCCEnv_collectCCs env) $ do
        lift . modify $ \s ->
          s { lateCCState_ccs = S.insert cc (lateCCState_ccs s)
            }
    return $ mkTick note expr

