content: module System.Taffybar.Widget.Text.MemoryMonitor (textMemoryMonitorNew, showMemoryInfo) where
  
  import Control.Monad.IO.Class ( MonadIO )
  import qualified Data.Text as T
  import qualified Text.StringTemplate as ST
  import System.Taffybar.Information.Memory
  import System.Taffybar.Widget.Generic.PollingLabel ( pollingLabelNew )
  import qualified GI.Gtk
  import Text.Printf ( printf )
  
  -- | Creates a simple textual memory monitor. It updates once every polling
  -- period (in seconds).
  textMemoryMonitorNew :: MonadIO m
                       => String -- ^ Format. You can use variables: "used", "total", "free", "buffer",
                                 -- "cache", "rest", "available", "swapUsed", "swapTotal", "swapFree".
                       -> Double -- ^ Polling period in seconds.
                       -> m GI.Gtk.Widget
  textMemoryMonitorNew fmt period = do
      label <- pollingLabelNew period (showMemoryInfo fmt 3 <$> parseMeminfo)
      GI.Gtk.toWidget label
  
  showMemoryInfo :: String -> Int -> MemoryInfo -> T.Text
  showMemoryInfo fmt prec info =
    let template = ST.newSTMP fmt
        labels = [ "used"
                 , "total"
                 , "free"
                 , "buffer"
                 , "cache"
                 , "rest"
                 , "available"
                 , "swapUsed"
                 , "swapTotal"
                 , "swapFree"
                 ]
        actions = [ memoryUsed
                  , memoryTotal
                  , memoryFree
                  , memoryBuffer
                  , memoryCache
                  , memoryRest
                  , memoryAvailable
                  , memorySwapUsed
                  , memorySwapTotal
                  , memorySwapFree
                  ]
        actions' = map (toAuto prec .) actions
        stats = [f info | f <- actions']
        template' = ST.setManyAttrib (zip labels stats) template
    in ST.render template'
  
  toAuto :: Int -> Double -> String
  toAuto prec value = printf "%.*f%s" p v unit
    where value' = max 0 value
          mag :: Int
          mag = if value' == 0 then 0 else max 0 $ min 2 $ floor $ logBase 1024 value'
          v = value' / 1024 ** fromIntegral mag
          unit = case mag of
            0 -> "MiB"
            1 -> "GiB"
            2 -> "TiB"
            _ -> "??B" -- unreachable
          p :: Int
          p = max 0 $ floor $ fromIntegral prec - logBase 10 v
  