-- | Generic code to poll any of the many data files maintained by the kernel in
-- POSIX systems. Provides methods for applying a custom parsing function to the
-- contents of the file and to calculate differentials across one or more values
-- provided via the file.
module System.Information.StreamInfo
    ( getParsedInfo
    , getLoad
    , getTransfer) where

import Control.Concurrent (threadDelay)
import Data.Maybe (fromJust)

-- | Apply the given parser function to the file under the given path to produce
-- a lookup map, then use the given selector as key to extract from it the
-- desired value.
getParsedInfo :: FilePath -> (String -> [(String, [Integer])]) -> String -> IO [Integer]
getParsedInfo path parser selector = do
    file <- readFile path
    (length file) `seq` return ()
    return (fromJust $ lookup selector $ parser file)

truncVal :: Double -> Double
truncVal v
  | isNaN v || v < 0.0 = 0.0
  | otherwise = v

-- | Execute the given action twice with the given delay in-between and return
-- the difference between the two samples.
probe :: IO [Integer] -> Double -> IO [Integer]
probe action delay = do
    a <- action
    threadDelay $ round (delay * 1e6)
    b <- action
    return $ zipWith (-) b a

-- | Probe the given action and, interpreting the result as a variation in time,
-- return the speed of change of its values.
getTransfer :: Double -> IO [Integer] -> IO [Double]
getTransfer interval action = do
    deltas <- probe action interval
    return $ map (truncVal . (/interval) . fromIntegral) deltas

-- | Probe the given action and return the relative variation of each of the
-- obtained values against the whole, where the whole is calculated as the sum
-- of all the values in the probe.
getLoad :: Double -> IO [Integer] -> IO [Double] 
getLoad interval action = do
    deltas <- probe action interval
    let total  = fromIntegral $ foldr (+) 0 deltas
        ratios = map ((/total) . fromIntegral) deltas
    return $ map truncVal ratios