    module System.Taffybar.Hooks
      ( module System.Taffybar.DBus
      , module System.Taffybar.Hooks
      , refreshBatteriesOnPropChange
      ) where

    import           Control.Applicative
    import           Control.Concurrent
    import           Control.Monad
    import           Control.Monad.Trans.Class
    import           Control.Monad.Trans.Reader
    import           Data.Maybe
    import qualified Data.MultiMap as MM
    import           System.FilePath
    import           System.Taffybar.Context
    import           System.Taffybar.DBus
    import           System.Taffybar.Information.Battery
    import           System.Taffybar.Information.Network
    import           System.Taffybar.Information.XDG.DesktopEntry
    import           System.Taffybar.Util

    newtype NetworkInfoChan = NetworkInfoChan (Chan [(String, (Rational, Rational))])

    buildInfoChan :: Double -> IO NetworkInfoChan
    buildInfoChan interval = do
      chan <- newChan
      _ <- forkIO $ monitorNetworkInterfaces interval $ writeChan chan
      return $ NetworkInfoChan chan

    getNetworkChan :: TaffyIO NetworkInfoChan
    getNetworkChan = getStateDefault $ lift $ buildInfoChan 2.0

    withBatteryRefresh :: TaffybarConfig -> TaffybarConfig
    withBatteryRefresh = appendHook refreshBatteriesOnPropChange

    getDirectoryEntriesByClassName :: TaffyIO (MM.MultiMap String DesktopEntry)
    getDirectoryEntriesByClassName =
      getStateDefault readDirectoryEntriesDefault

    updateDirectoryEntriesCache :: TaffyIO ()
    updateDirectoryEntriesCache = ask >>= \ctx ->
      void $ lift $ foreverWithDelay (60 :: Double) $ flip runReaderT ctx $
           putState readDirectoryEntriesDefault

    readDirectoryEntriesDefault :: TaffyIO (MM.MultiMap String DesktopEntry)
    readDirectoryEntriesDefault = lift $
      directoryEntriesByClassName <$> getDirectoryEntriesDefault

    directoryEntriesByClassName
      :: Foldable t
      => t DesktopEntry -> MM.MultiMap String DesktopEntry
    directoryEntriesByClassName = foldl insertByClassName MM.empty
      where
        insertByClassName entriesMap entry =
          MM.insert (getClassName entry) entry entriesMap
        getFromFilename filepath =
          let (_, filename) = splitFileName filepath
              (_, noExtensions) = splitExtensions filename
          in noExtensions
        getClassName DesktopEntry {deAttributes = attributes, deFilename = filename} =
          fromMaybe (getFromFilename filename) $
                    lookup "StartupWMClass" attributes <|>
                    lookup "Name" attributes