module System.Taffybar.Widget.Util where
import           Control.Concurrent ( forkIO )
import           Control.Monad ( when, forever, void )
import           Control.Monad.IO.Class
import           Control.Monad.Trans.Maybe
import           Data.Functor ( ($>) )
import           Data.Int
import qualified Data.Text as T
import           Data.Tuple.Sequence
import qualified GI.GdkPixbuf.Objects.Pixbuf as GI
import qualified GI.GdkPixbuf.Objects.Pixbuf as PB
import qualified GI.Gtk
import           Graphics.UI.Gtk as Gtk
import           Graphics.UI.Gtk.General.StyleContext
import           System.Directory
import           System.FilePath.Posix
import           System.Taffybar.Information.XDG.DesktopEntry
import           System.Taffybar.Util
import           Text.Printf
import           Paths_taffybar ( getDataDir )
onClick :: [Click] 
        -> IO a    
        -> EventM EButton Bool
onClick triggers action = tryEvent $ do
  click <- eventClick
  when (click `elem` triggers) $ void $ liftIO action
attachPopup :: (WidgetClass w, WindowClass wnd) =>
               w      
            -> String 
            -> wnd    
            -> IO ()
attachPopup widget title window = do
  set window [ windowTitle := title
             , windowTypeHint := WindowTypeHintTooltip
             , windowSkipTaskbarHint := True
             , windowSkipPagerHint := True
             , windowTransientFor :=> getWindow
             ]
  windowSetKeepAbove window True
  windowStick window
  where getWindow = do
          Just topLevelWindow <- fmap castToWindow <$> widgetGetAncestor widget gTypeWindow
          return topLevelWindow
displayPopup :: (WidgetClass w, WindowClass wnd) =>
                w   
             -> wnd 
             -> IO ()
displayPopup widget window = do
  windowSetPosition window WinPosMouse
  (x, y ) <- windowGetPosition window
  (_, y') <- widgetGetSizeRequest widget
  widgetShowAll window
  if y > y'
    then windowMove window x (y - y')
    else windowMove window x y'
widgetGetAllocatedSize
  :: (WidgetClass self, MonadIO m)
  => self -> m (Int, Int)
widgetGetAllocatedSize widget =
  liftIO $
  sequenceT (widgetGetAllocatedWidth widget, widgetGetAllocatedHeight widget)
colorize :: String 
         -> String 
         -> String 
         -> String
colorize fg bg = printf "<span%s%s>%s</span>" (attr "fg" fg) (attr "bg" bg)
  where attr name value
          | null value = ""
          | otherwise  = printf " %scolor=\"%s\"" name value
backgroundLoop :: IO a -> IO ()
backgroundLoop = void . forkIO . forever
drawOn :: WidgetClass object => object -> IO () -> IO object
drawOn drawArea action = on drawArea realize action $> drawArea
widgetSetClass
  :: (Gtk.WidgetClass widget, MonadIO m)
  => widget -> String -> m widget
widgetSetClass widget klass = liftIO $ do
  context <- Gtk.widgetGetStyleContext widget
  styleContextAddClass context klass
  return widget
widgetSetClassGI :: (GI.Gtk.IsWidget b, MonadIO m) => b -> T.Text -> m b
widgetSetClassGI widget klass =
  GI.Gtk.widgetGetStyleContext widget >>=
    flip GI.Gtk.styleContextAddClass klass >> return widget
themeLoadFlags :: [GI.Gtk.IconLookupFlags]
themeLoadFlags =
  [ GI.Gtk.IconLookupFlagsGenericFallback
  , GI.Gtk.IconLookupFlagsUseBuiltin
  ]
getImageForDesktopEntry :: Int32 -> DesktopEntry -> IO (Maybe GI.Pixbuf)
getImageForDesktopEntry size entry = runMaybeT $ do
  iconName <- MaybeT $ return $ deIcon entry
  let iconNameText = T.pack iconName
  MaybeT $ do
    iconTheme <- GI.Gtk.iconThemeGetDefault
    hasIcon <- GI.Gtk.iconThemeHasIcon iconTheme iconNameText
    logPrintFDebug "System.Taffybar.Widget.Util" "Entry: %s" entry
    logPrintFDebug "System.Taffybar.Widget.Util" "Icon present: %s" hasIcon
    if hasIcon
    then
      GI.Gtk.iconThemeLoadIcon iconTheme iconNameText size themeLoadFlags
    else do
      exists <- doesFileExist iconName
      if isAbsolute iconName && exists
      then Just <$> GI.pixbufNewFromFile iconName
      else return Nothing
loadPixbufByName :: Int32 -> T.Text -> IO (Maybe GI.Pixbuf)
loadPixbufByName size name = do
  iconTheme <- GI.Gtk.iconThemeGetDefault
  hasIcon <- GI.Gtk.iconThemeHasIcon iconTheme name
  if hasIcon
  then GI.Gtk.iconThemeLoadIcon iconTheme name size themeLoadFlags
  else return Nothing
alignCenter :: (GI.Gtk.IsWidget o, MonadIO m) => o -> m ()
alignCenter widget =
  GI.Gtk.setWidgetValign widget GI.Gtk.AlignCenter >>
  GI.Gtk.setWidgetHalign widget GI.Gtk.AlignCenter
vFillCenter :: (GI.Gtk.IsWidget o, MonadIO m) => o -> m ()
vFillCenter widget =
  GI.Gtk.widgetSetVexpand widget True >>
  GI.Gtk.setWidgetValign widget GI.Gtk.AlignFill >>
  GI.Gtk.setWidgetHalign widget GI.Gtk.AlignCenter
pixbufNewFromFileAtScaleByHeight :: Int32 -> String -> IO PB.Pixbuf
pixbufNewFromFileAtScaleByHeight height name =
  PB.pixbufNewFromFileAtScale name (-1) height True
loadIcon :: Int32 -> String -> IO PB.Pixbuf
loadIcon height name =
  ((</> "icons" </> name) <$> getDataDir) >>=
  pixbufNewFromFileAtScaleByHeight height
setMinWidth :: (Gtk.WidgetClass w, MonadIO m) => Int -> w -> m w
setMinWidth width widget = liftIO $ do
  Gtk.widgetSetSizeRequest widget width (-1)
  return widget