content: {-# LANGUAGE MonoLocalBinds #-}
  {-# LANGUAGE TemplateHaskell #-}
  {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE OverloadedLabels #-}
  
  module NotificationCenter.Notifications.AbstractNotification
    ( createNotification
    , setUrgencyLevel
    , updateNotiContent
    , HasDisplayingNotificationContent(..)
    , DisplayingNotificationContent(..)
    ) where
  
  import Helpers (atMay, markupify)
  import Config (Config(..))
  import NotificationCenter.Notifications.Data
    (Urgency(..), CloseType(..), Notification(..), Image(..), rawImgToPixBuf)
  import NotificationCenter.Notifications.Action
    (Action(..), createAction)
  import TransparentWindow (scale, label, image, box, getObjs, addClass, progressbar)
  
  import Data.Text as Text
  import Data.Int ( Int32 )
  import Data.Maybe (fromMaybe)
  
  import Control.Lens.TH (makeClassy)
  import Control.Lens (view, set)
  import Control.Monad (when)
  
  import GI.Gtk (rangeGetValue, onRangeValueChanged, rangeSetValue, widgetShowAll, widgetHide, windowMove, widgetDestroy
                , widgetSetValign, widgetSetMarginStart, widgetSetMarginEnd
                , widgetSetMarginTop, widgetSetMarginBottom, labelSetText
                , labelSetMarkup, widgetSetSizeRequest, labelSetXalign
                , widgetGetPreferredHeightForWidth, onWidgetButtonPressEvent
                , imageSetFromPixbuf, imageSetFromIconName, setWidgetWidthRequest
                , setImagePixelSize, widgetSetMarginStart, widgetSetMarginEnd
                , progressBarSetFraction, widgetSetVisible
                , catchGErrorJustDomain, GErrorMessage(..))
  import GI.GLib (FileError(..))
  import GI.GdkPixbuf (pixbufScaleSimple, pixbufGetHeight, pixbufGetWidth
                      , Pixbuf(..), pixbufNewFromFileAtScale
                      , InterpType(..), PixbufError(..))
  import qualified GI.Gtk as Gtk
    (Scale, ProgressBar, IsWidget, Box(..), Label(..), Button(..), Window(..), Image(..)
    , Builder(..), containerAdd, containerRemove, containerGetChildren)
  import GI.Gtk.Enums (Align(..))
  
  data DisplayingNotificationContent = DisplayingNotificationContent
    { _dLabelTitel :: Gtk.Label
    , _dLabelBody :: Gtk.Label
    , _dLabelAppname :: Gtk.Label
    , _dImgAppIcon :: Gtk.Image
    , _dImgImage :: Gtk.Image
    , _dContainer :: Gtk.Box
    , _dActions :: Gtk.Box
    , _dProgressbar :: Gtk.ProgressBar
    , _dScale :: Gtk.Scale
    }
  makeClassy ''DisplayingNotificationContent
  
  
  
  createNotification :: HasDisplayingNotificationContent dn =>
    Config -> Gtk.Builder -> Notification -> dn -> IO dn
  createNotification config builder noti dispNoti = do
  
    objs <- getObjs builder [ "label_titel"
                            , "label_body"
                            , "label_appname"
                            , "img_icon"
                            , "img_img"
                            , "box_container"
                            , "box_actions"
                            , "progressbar"
                            , "scale"]
  
    labelTitel <- label objs "label_titel"
    labelBody <- label objs "label_body"
    labelAppname <- label objs "label_appname"
    container <- box objs "box_container"
    actions <- box objs "box_actions"
    imgAppIcon <- image objs "img_icon"
    imgImage <- image objs "img_img"
    progressBar <- progressbar objs "progressbar"
    scale <- scale objs "scale"
  
    -- set margins from config
    widgetSetMarginTop imgImage
      (fromIntegral $ configImgMarginTop config)
    widgetSetMarginBottom imgImage
      (fromIntegral $ configImgMarginBottom config)
    widgetSetMarginStart imgImage
      (fromIntegral $ configImgMarginLeft config)
    widgetSetMarginEnd imgImage
      (fromIntegral $ configImgMarginRight config)
  
    onWidgetButtonPressEvent container $ \(_) -> do
      notiOnAction noti (notiActionCommands noti) "default" Nothing
      return False
  
  
    return
      $ set dLabelTitel labelTitel
      $ set dLabelBody labelBody
      $ set dLabelAppname labelAppname
      $ set dImgAppIcon imgAppIcon
      $ set dImgImage imgImage
      $ set dContainer container
      $ set dActions actions
      $ set dProgressbar progressBar
      $ set dScale scale
      dispNoti
  
  setUrgencyLevel :: Gtk.IsWidget widget => Urgency -> [widget] -> IO ()
  setUrgencyLevel urgency elems = do
    case urgency of
      High -> do
        sequence $ (flip addClass) "critical" <$> elems
      Low -> do
        sequence $ (flip addClass) "low" <$> elems
      Normal -> do
        sequence $ (flip addClass) "normal" <$> elems
    return ()
  
  
  updateNotiContent :: HasDisplayingNotificationContent dn
    => Config -> Notification -> dn -> IO ()
  updateNotiContent config noti dNoti = do
    labelSetText (view dLabelTitel dNoti) $ notiSummary noti
  
    if configNotiMarkup config
      then labelSetMarkup (view dLabelBody dNoti) . markupify $ notiBody noti
      else labelSetText (view dLabelBody dNoti) $ notiBody noti
  
    when (configPopupHideBodyIfEmpty config && notiBody noti == "") $ do
      widgetSetVisible (view dLabelBody dNoti) False
  
    if notiAppName noti /= "" then
      labelSetText (view dLabelAppname dNoti) $ notiAppName noti
      else
      widgetSetVisible (view dLabelAppname dNoti) False
    labelSetXalign (view dLabelTitel dNoti) 0
    labelSetXalign (view dLabelBody dNoti) 0
  
    if notiIcon noti /= NoImage then
      setImage (notiIcon noti) (fromIntegral $ configIconSize config)
      $ view dImgAppIcon dNoti
      else
      widgetSetVisible (view dImgAppIcon dNoti) False
  
    setImage (notiImg noti) (fromIntegral $ notiImgSize noti)
      $ view dImgImage dNoti
  
    let takeTwo (a:b:cs) = (a,b):(takeTwo cs)
        takeTwo _ = []
    actionButtons <- sequence
      $ (\(a, b) -> createAction
                    config
                    (notiActionCommands noti)
                    (notiActionIcons noti)
                    (notiOnAction noti)
                    20
                    20
                    a
                    b)
      <$> (Prelude.filter (\(a, b) -> a /= "default"
                             && (notiPercentage noti == Nothing
                                 || a /= "changeValue"))
           $ takeTwo (unpack <$> notiActions noti))
    currentButtons <- Gtk.containerGetChildren (view dActions dNoti)
    sequence $ Gtk.containerRemove (view dActions dNoti) <$> currentButtons
    sequence $ Gtk.containerAdd (view dActions dNoti) <$> actionButton <$> actionButtons
  
    addClass (view dContainer dNoti) (notiClassName noti)
  
    widgetShowAll (view dActions dNoti)
  
  
    if (notiPercentage noti /= Nothing) then do
      if (onChangeAction == Nothing) then do
        progressBarSetFraction (view dProgressbar dNoti)
          ((fromMaybe 0 $ notiPercentage noti) / 100.0)
        widgetSetVisible (view dProgressbar dNoti) True
        widgetSetVisible (view dScale dNoti) False
        return ()
        else do
        rangeSetValue (view dScale dNoti)
          (fromMaybe 0 $ notiPercentage noti)
        onRangeValueChanged (view dScale dNoti) $ do
          value <- rangeGetValue (view dScale dNoti)
          (notiOnAction noti) (notiActionCommands noti) "changeValue" $ Just $ show value
          return ()
        widgetSetVisible (view dScale dNoti) True
        widgetSetVisible (view dProgressbar dNoti) False
        return ()
      else do
      widgetSetVisible (view dProgressbar dNoti) False
      widgetSetVisible (view dScale dNoti) False
    return ()
    where  onChangeAction = atMay
             (Prelude.filter (\(a, b) -> a == "changeValue")
              $ takeTwo (unpack <$> notiActions noti)) 0
           takeTwo (a:b:cs) = (a,b):(takeTwo cs)
           takeTwo _ = []
  
  
  
  setImage :: Image -> Int32 -> Gtk.Image -> IO ()
  setImage image imageSize widget = do
    case image of
      NoImage -> do
        widgetSetMarginStart widget 0
        widgetSetMarginEnd widget 0
        widgetSetVisible widget False
      (ImagePath path) -> do
        pb <- catchGErrorJustDomain
              (catchGErrorJustDomain
               (pixbufNewFromFileAtScale path imageSize imageSize True)
               ((\err message -> return Nothing)
                :: PixbufError -> GErrorMessage -> IO (Maybe Pixbuf)))
              ((\err message -> return Nothing)
                :: FileError -> GErrorMessage -> IO (Maybe Pixbuf))
        case pb of
          (Just pb') -> imageSetFromPixbuf widget (Just pb')
          Nothing -> return ()
      (NamedIcon name) -> do
        imageSetFromIconName widget
          (Just $ pack name) imageSize
        setImagePixelSize widget imageSize
      (RawImg a) -> do
        pb <- rawImgToPixBuf $ RawImg a
        pb' <- scalePixbuf imageSize imageSize pb
        imageSetFromPixbuf widget pb'
  
  
  scalePixbuf :: Int32 -> Int32 -> Pixbuf -> IO (Maybe Pixbuf)
  scalePixbuf w h pb = do
    oldW <- fromIntegral <$> pixbufGetWidth pb :: IO Double
    oldH <- fromIntegral <$> pixbufGetHeight pb :: IO Double
    let targetW = fromIntegral w :: Double
        targetH = fromIntegral h :: Double
    if (oldW < targetW || oldH < targetH) then
      return (Just pb)
      else do
      let newW = fromIntegral $ floor $ if oldW > oldH then
            targetW else (oldW * (targetH / oldH))
          newH = fromIntegral $ floor $ if oldW > oldH then
            (oldH * (targetW / oldW)) else targetH
      pixbufScaleSimple pb newW newH InterpTypeBilinear
  