content: {-|
  Module      : Monomer.Widgets.Animation.Fade
  Copyright   : (c) 2018 Francisco Vallarino
  License     : BSD-3-Clause (see the LICENSE file)
  Maintainer  : fjvallarino@gmail.com
  Stability   : experimental
  Portability : non-portable
  
  Fade animation widget. Wraps a child widget whose content will be animated.
  
  Messages:
  
  - Accepts an 'AnimationMsg', used to control the state of the animation.
  -}
  {-# LANGUAGE FlexibleInstances #-}
  {-# LANGUAGE MultiParamTypeClasses #-}
  {-# LANGUAGE RecordWildCards #-}
  {-# LANGUAGE Strict #-}
  
  module Monomer.Widgets.Animation.Fade (
    -- * Configuration
    FadeCfg,
    -- * Constructors
    animFadeIn,
    animFadeIn_,
    animFadeOut,
    animFadeOut_
  ) where
  
  import Control.Lens ((&), (.~))
  import Data.Default
  import Data.Maybe
  
  import Monomer.Widgets.Container
  import Monomer.Widgets.Animation.Transform
  
  import qualified Monomer.Lens as L
  
  {-|
  Configuration options for fade:
  
  - 'autoStart': whether the first time the widget is added, animation should run.
  - 'duration': how long the animation lasts in ms.
  - 'onFinished': event to raise when animation is complete.
  - 'onFinishedReq': 'WidgetRequest' to generate when animation is complete.
  -}
  newtype FadeCfg s e = FadeCfg {
    _fdcTransformCfg :: TransformCfg s e
  } deriving (Eq, Show)
  
  instance Default (FadeCfg s e) where
    def = FadeCfg {
      _fdcTransformCfg = def
    }
  
  instance Semigroup (FadeCfg s e) where
    (<>) fc1 fc2 = FadeCfg {
      _fdcTransformCfg = _fdcTransformCfg fc1 <> _fdcTransformCfg fc2
    }
  
  instance Monoid (FadeCfg s e) where
    mempty = def
  
  instance CmbAutoStart (FadeCfg s e) where
    autoStart_ start = def {
      _fdcTransformCfg = autoStart_ start
    }
  
  instance CmbDuration (FadeCfg s e) Millisecond where
    duration dur = def {
      _fdcTransformCfg = duration dur
    }
  
  instance WidgetEvent e => CmbOnFinished (FadeCfg s e) e where
    onFinished handler = def {
      _fdcTransformCfg = onFinished handler
    }
  
  instance CmbOnFinishedReq (FadeCfg s e) s e where
    onFinishedReq req = def {
      _fdcTransformCfg = onFinishedReq req
    }
  
  -- | Animates a widget from not visible state to fully visible.
  animFadeIn
    :: WidgetEvent e
    => WidgetNode s e  -- ^ The child node.
    -> WidgetNode s e  -- ^ The created animation container.
  animFadeIn managed = animFadeIn_ def managed
  
  -- | Animates a widget from not visible state to fully visible. Accepts config.
  animFadeIn_
    :: WidgetEvent e
    => [FadeCfg s e]   -- ^ The config options.
    -> WidgetNode s e  -- ^ The child node.
    -> WidgetNode s e  -- ^ The created animation container.
  animFadeIn_ configs managed = makeNode configs managed True
    & L.info . L.widgetType .~ "animFadeIn"
  
  -- | Animates a widget from visible state to not visible.
  animFadeOut
    :: WidgetEvent e
    => WidgetNode s e  -- ^ The child node.
    -> WidgetNode s e  -- ^ The created animation container.
  animFadeOut managed = animFadeOut_ def managed
  
  -- | Animates a widget from visible state to not visible. Accepts config.
  animFadeOut_
    :: WidgetEvent e
    => [FadeCfg s e]   -- ^ The config options.
    -> WidgetNode s e  -- ^ The child node.
    -> WidgetNode s e  -- ^ The created animation container.
  animFadeOut_ configs managed = makeNode configs managed False
    & L.info . L.widgetType .~ "animFadeOut"
  
  makeNode
    :: WidgetEvent e
    => [FadeCfg s e]
    -> WidgetNode s e
    -> Bool
    -> WidgetNode s e
  makeNode configs managed isFadeIn = node where
    node = animTransform_ [_fdcTransformCfg] f managed
    f t _ = [animGlobalAlpha $ alpha t]
    alpha t = if isFadeIn
      then (currStep t)
      else 1-(currStep t)
    currStep t = clampAlpha $ t/(fromIntegral dur)
    dur = fromMaybe 500 _tfcDuration
    TransformCfg{..} = _fdcTransformCfg
    FadeCfg{..} = mconcat configs
  