-- | The provided data type 'STerm' is intended to store a term equipped with
--   its own name and maybe some extra information about its cost center
--   annotation and modules that need to be loaded when evaluating the term.
--
--   The additional information, provided as strings, is necessary for space 
--   measurements where small programs are constructed from the strings and run
--   for heap profiling.

module Test.SBench.STerm
  ( CostCenter
  , DataGen
  , Algorithm
  , Data
  , Seed 
  , Test
  , STerm (..)
  , toData
  , toNamedData
  , toDataGen
  , toAlgorithm
  , (<$>)
  , getImports
  , makeSeeds
  , makeIntSeeds
  ) where

import Data.List ( nub )

type CostCenter = String
type ModuleName = String
type TermName   = String

data STerm a = T { stTerm    :: a             -- ^ The actual term.
                 , stName    :: TermName      -- ^ The term as 'String'
                 , stModules :: [ModuleName]  -- ^ The modules used when evaluation the term
                 , stCC      :: [CostCenter]  -- ^ cost centers to measure when performing 
                                              --   heap profiling.
                 }

instance Show (STerm a) where
   show t = stName t

-- Aliases for 'STerm', all suggesting different use.
type Algorithm a = STerm a
type DataGen   a = STerm a
type Data      a = STerm a
type Seed      a = STerm a
type Test a b = Data a -> FilePath -> IO b


-- Auxiliar generator functions for 'STerm's.
toAlgorithm :: (a -> b) -> ModuleName -> TermName -> CostCenter -> Algorithm (a -> b)
toAlgorithm alg mn tn cc = T alg (mn ++ "." ++ tn) [mn] [cc]

toDataGen :: (a -> b) -> ModuleName -> TermName -> Data (a -> b)
toDataGen f "" tn = T f tn [] []
toDataGen f mn tn = T f (mn ++ "." ++ tn) [mn] []

toData :: (Show a) => a -> STerm a
toData a = T a (show a) [] []

toNamedData :: (Show a) => a -> TermName -> STerm a
toNamedData a n = T a n [] []

makeSeeds :: (Show a, Integral a) 
          => a -- ^ minimal value
          -> a -- ^ maximal value
          -> a -- ^ number of seeds
          -> [Seed a]
makeSeeds min max steps = map toData [min, (min + ((max - min) `div` steps)) .. max]

-- | Auxiliar version of 'makeSeeds' to prevent defaulting to 'Integer'.
makeIntSeeds :: Int -> Int -> Int -> [Seed Int]
makeIntSeeds = makeSeeds


-- | Function application for 'STerm'.
(<$>) :: STerm (a -> b) -> STerm a -> STerm b
(<$>) (T f sf ms1 cc1) (T a sa ms2 cc2) =
    T (f a) (sf ++ " " ++ sa) (nub (ms1 ++ ms2)) (nub (cc1 ++ cc2))

getImports :: STerm a -> String
getImports = unlines . map ("import qualified " ++) . stModules