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
