    -----------------------------------------------------------------------------
    -- |
    -- Module      : System.Taffybar.NetMonitor
    -- Copyright   : (c) José A. Romero L.
    -- License     : BSD3-style (see LICENSE)
    --
    -- Maintainer  : José A. Romero L. <escherdragon@gmail.com>
    -- Stability   : unstable
    -- Portability : unportable
    --
    -- Simple text widget that displays incoming\/outgoing network traffic over
    -- one selected interface, as provided by the "System.Information.Network"
    -- module.
    --
    -----------------------------------------------------------------------------

    module System.Taffybar.NetMonitor (
      netMonitorNew,
      netMonitorNewWith,
      defaultNetFormat
      ) where

    import           Data.IORef
    import           Graphics.UI.Gtk
    import           System.Information.Network           (getNetInfo)
    import           System.Taffybar.Widgets.PollingLabel
    import           Text.Printf                          (printf)
    import           Text.StringTemplate

    defaultNetFormat :: String
    defaultNetFormat = "▼ $inKB$kb/s ▲ $outKB$kb/s"

    -- | Creates a new network monitor widget. It consists of two 'PollingLabel's,
    -- one for incoming and one for outgoing traffic fed by regular calls to
    -- 'getNetInfo'.
    netMonitorNew :: Double -- ^ Polling interval (in seconds, e.g. 1.5)
                  -> String -- ^ Name of the network interface to monitor (e.g. \"eth0\", \"wlan1\")
                  -> IO Widget
    netMonitorNew interval interface =
      netMonitorNewWith interval interface 2 defaultNetFormat

    -- | Creates a new network monitor widget with custom template and precision.
    -- Similar to 'netMonitorNew'.
    --
    -- The format template currently supports three units: bytes,
    -- kilobytes, and megabytes.  Automatic intelligent unit selection is
    -- planned, eventually.
    netMonitorNewWith :: Double -- ^ Polling interval (in seconds, e.g. 1.5)
                      -> String -- ^ Name of the network interface to monitor (e.g. \"eth0\", \"wlan1\")
                      -> Integer -- ^ Precision for an output
                      -> String -- ^ Template for an output. You can use variables: $inB$, $inKB$, $inMB$, $outB$, $outKB$, $outMB$
                      -> IO Widget
    netMonitorNewWith interval interface prec template = do
        sample <- newIORef [0, 0]
        label  <- pollingLabelNew "" interval $ showInfo sample interval interface template prec
        widgetShowAll label
        return $ toWidget label

    showInfo :: IORef [Integer] -> Double -> String -> String -> Integer -> IO String
    showInfo sample interval interface template prec = do
        maybeThisSample <- getNetInfo interface
        case maybeThisSample of
          Nothing -> return ""
          Just thisSample -> do
            lastSample <- readIORef sample
            writeIORef sample thisSample
            let deltas = map (max 0 . fromIntegral) $ zipWith (-) thisSample lastSample
                speed@[incomingb, outgoingb] = map (/(interval)) deltas
                [incomingkb, outgoingkb] = map (setDigits prec . (/1024)) speed
                [incomingmb, outgoingmb] = map (setDigits prec . (/square 1024)) speed
                attribs = [ ("inB", show incomingb)
                          , ("inKB", incomingkb)
                          , ("inMB", incomingmb)
                          , ("outB", show outgoingb)
                          , ("outKB", outgoingkb)
                          , ("outMB", outgoingmb)
                          ]
            return . render . setManyAttrib attribs $ newSTMP template

    square :: Double -> Double
    square x = x ^ (2 :: Int)

    setDigits :: Integer -> Double -> String
    setDigits dig a = printf format a
        where format = "%." ++ show dig ++ "f"