content: {-
      Copyright 2016,2017 Markus Ongyerth
  
      This file is part of Monky.
  
      Monky is free software: you can redistribute it and/or modify
      it under the terms of the GNU Lesser General Public License as published by
      the Free Software Foundation, either version 3 of the License, or
      (at your option) any later version.
  
      Monky is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU Lesser General Public License for more details.
  
      You should have received a copy of the GNU Lesser General Public License
      along with Monky.  If not, see <http://www.gnu.org/licenses/>.
  -}
  {-# LANGUAGE OverloadedStrings #-}
  {-|
  Module      : Monky.Examples.Utility
  Description : Provides utility functions for module implementations
  Maintainer  : ongy
  Stability   : testing
  Portability : Linux
  
  This module provides utility functions used in monky modules
  -}
  module Monky.Examples.Utility
   ( loopFd
   , convertUnit
   , convertUnitB
   , convertUnitSI
   )
  where
  
  import Data.Text (Text)
  import qualified Data.Text as T
  import Formatting
  
  import Control.Monad (unless)
  import Control.Concurrent (threadWaitRead)
  import Data.Scientific (fromFloatDigits, FPFormat(Exponent))
  import System.Posix.Types (Fd)
  import Monky.Modules
  
  
  -- |Utility Function for eventing modules
  loopFd
    :: h -- ^Some kind of module handle
    -> Fd -- ^The FD to block on
    -> ([MonkyOut] -> IO ()) -- ^The update consume function
    -> (h -> IO [MonkyOut]) -- ^The function to generate the output
    -> IO () -- ^This will loop for you
  loopFd h fd r f = do
    threadWaitRead fd
    out <- f h
    unless (null out) (r out)
    loopFd h fd r f
  
  
  -- |Convert a number into a fixed length strings
  convertUnit :: Int -> Text -> Text -> Text -> Text -> Text
  convertUnit = flip convertUnitT 1000 . fromIntegral
  
  
  -- |Convert a number into a reasonable scale for binary units
  convertUnitB :: Integral a => a -> Text -> Text
  convertUnitB rate b = convertUnitT (fromIntegral rate) 1024 (" " `T.append` b) "ki" "Mi" "Gi"
  
  
  -- |Convert a number into a reasonable scale for SI units
  convertUnitSI :: Integral a => a -> Text -> Text
  convertUnitSI rate b = convertUnitT (fromIntegral rate) 1000 b "k" "M" "G"
  
  convertUnitT :: Double -> Int -> Text -> Text -> Text -> Text -> Text
  convertUnitT rate step bs ks ms gs
    | rate < fromIntegral (kf       ) = sformat ((left 4 ' ' %. fixed 0) % stext) rate bs
    | rate < fromIntegral (kf * 10  ) = sformat ((left 4 ' ' %. fixed 2) % stext) kv ks
    | rate < fromIntegral (kf * 100 ) = sformat ((left 4 ' ' %. fixed 1) % stext) kv ks
    | rate < fromIntegral (kf * 1000) = sformat ((left 4 ' ' %. fixed 0) % stext) kv ks
    | rate < fromIntegral (mf * 10  ) = sformat ((left 4 ' ' %. fixed 2) % stext) mv ms
    | rate < fromIntegral (mf * 100 ) = sformat ((left 4 ' ' %. fixed 1) % stext) mv ms
    | rate < fromIntegral (mf * 1000) = sformat ((left 4 ' ' %. fixed 0) % stext) mv ms
    | rate < fromIntegral (gf * 10  ) = sformat ((left 4 ' ' %. fixed 2) % stext) gv gs
    | rate < fromIntegral (gf * 100 ) = sformat ((left 4 ' ' %. fixed 1) % stext) gv gs
    | rate < fromIntegral (gf * 1000) = sformat ((left 4 ' ' %. fixed 0) % stext) gv gs
    | otherwise                       = sformat ((left 4 ' ' %. scifmt Exponent Nothing) % stext) (fromFloatDigits gv) gs
    where
      kf = 1  * step
      mf = kf * step
      gf = mf * step
      kv = rate / fromIntegral kf
      mv = rate / fromIntegral mf
      gv = rate / fromIntegral gf
  
  
  