module System.Taffybar.SimpleConfig
  ( SimpleTaffyConfig(..)
  , Position(..)
  , defaultSimpleTaffyConfig
  , simpleTaffybar
  , toTaffyConfig
  , useAllMonitors
  , usePrimaryMonitor
  ) where
import qualified Control.Concurrent.MVar as MV
import           Control.Monad.Trans.Class
import           Data.List
import           Data.Maybe
import           Data.Unique
import           Graphics.UI.GIGtkStrut
import           Graphics.UI.Gtk as Gtk
import           System.Taffybar.Information.X11DesktopInfo
import           System.Taffybar
import qualified System.Taffybar.Context as BC (BarConfig(..))
import           System.Taffybar.Context hiding (BarConfig(..))
import           System.Taffybar.Util
data Position = Top | Bottom deriving (Show, Eq)
data SimpleTaffyConfig = SimpleTaffyConfig
  {
  
    monitorsAction :: TaffyIO [Int]
  
  , barHeight :: Int
  
  , barPadding :: Int
  
  , barPosition :: Position
  
  , widgetSpacing :: Int
  
  , startWidgets :: [TaffyIO Gtk.Widget]
  
  , centerWidgets :: [TaffyIO Gtk.Widget]
  
  , endWidgets :: [TaffyIO Gtk.Widget]
  }
defaultSimpleTaffyConfig :: SimpleTaffyConfig
defaultSimpleTaffyConfig = SimpleTaffyConfig
  { monitorsAction = useAllMonitors
  , barHeight = 30
  , barPadding = 0
  , barPosition = Top
  , widgetSpacing = 5
  , startWidgets = []
  , centerWidgets = []
  , endWidgets = []
  }
toStrutConfig :: SimpleTaffyConfig -> Int -> StrutConfig
toStrutConfig SimpleTaffyConfig { barHeight = size
                                , barPadding = padding
                                , barPosition = pos
                                } monitor =
  defaultStrutConfig
  { strutHeight = ExactSize $ fromIntegral size
  , strutYPadding = fromIntegral padding
  , strutXPadding = fromIntegral padding
  , strutAlignment = Center
  , strutMonitor = Just $ fromIntegral monitor
  , strutPosition =
      case pos of
        Top -> TopPos
        Bottom -> BottomPos
  }
toBarConfig :: SimpleTaffyConfig -> Int -> IO BC.BarConfig
toBarConfig config monitor = do
  let strutConfig = toStrutConfig config monitor
  barId <- newUnique
  return
    BC.BarConfig
    { BC.strutConfig = strutConfig
    , BC.widgetSpacing = fromIntegral $ widgetSpacing config
    , BC.startWidgets = startWidgets config
    , BC.centerWidgets = centerWidgets config
    , BC.endWidgets = endWidgets config
    , BC.barId = barId
    }
newtype SimpleBarConfigs = SimpleBarConfigs (MV.MVar [(Int, BC.BarConfig)])
toTaffyConfig :: SimpleTaffyConfig -> TaffybarConfig
toTaffyConfig conf = defaultTaffybarConfig { getBarConfigsParam = configGetter }
  where
    configGetter = do
      SimpleBarConfigs configsVar <-
        getStateDefault $ lift (SimpleBarConfigs <$> MV.newMVar [])
      monitorNumbers <- monitorsAction conf
      let lookupWithIndex barConfigs monitorNumber =
            (monitorNumber, lookup monitorNumber barConfigs)
          lookupAndUpdate barConfigs = do
            let (alreadyPresent, toCreate) =
                  partition (isJust . snd) $
                  map (lookupWithIndex barConfigs) monitorNumbers
                alreadyPresentConfigs = mapMaybe snd alreadyPresent
            newlyCreated <-
              mapM (forkM return (toBarConfig conf) . fst) toCreate
            let result = map snd newlyCreated ++ alreadyPresentConfigs
            return (barConfigs ++ newlyCreated, result)
      lift $ MV.modifyMVar configsVar lookupAndUpdate
simpleTaffybar :: SimpleTaffyConfig -> IO ()
simpleTaffybar conf = dyreTaffybar $ toTaffyConfig conf
getMonitorCount :: IO Int
getMonitorCount = screenGetDefault >>= maybe (return 0) screenGetNMonitors
useAllMonitors :: TaffyIO [Int]
useAllMonitors = lift $ do
  count <- getMonitorCount
  return [0..count-1]
usePrimaryMonitor :: TaffyIO [Int]
usePrimaryMonitor =
  return . fromMaybe 0 <$> lift (withDefaultCtx getPrimaryOutputNumber)