module DzenDhall.Animation.Slider where

import DzenDhall.Config
import DzenDhall.AST
import DzenDhall.Data
import DzenDhall.Extra

import Data.Vector (Vector, (!), null, length)
import Lens.Micro

-- | There are three stages of the animation in a cycle:
--
-- 1. Fading in
-- 2. Delay
-- 3. Fading out
run
  :: Slider
  -> Int
  -- ^ Frame number
  -> Vector AST
  -- ^ Vector of possible ASTs (only one of them will be selected)
  -> AST
run _      _     asts
  | Data.Vector.null asts = mempty
-- Short-circuit if `fadeFrameCount`s are all set to zero.
run slider frame asts
  | slider ^. fadeIn  . fadeFrameCount == 0 &&
    slider ^. fadeOut . fadeFrameCount == 0 =
      let astIx       = (frame `div` positive (slider ^. sliderDelay))
                        `mod` Data.Vector.length asts
      in asts ! astIx
run slider frame asts =
  -- Calculate total frame counts for each 3 stages:
  let inFrameCount    = positive $ slider ^. fadeIn  . fadeFrameCount
      delayFrameCount =            slider ^. sliderDelay
      outFrameCount   = positive $ slider ^. fadeOut . fadeFrameCount

      -- Find how many frames total in a cycle
      totalFrames     = positive $
        inFrameCount + delayFrameCount + outFrameCount

      -- Find currenlty selected AST index
      astIx           = (frame `div` totalFrames) `mod` Data.Vector.length asts

      -- Select visible AST
      ast             = asts ! astIx

      -- Find in which frame we are, starting from the beginning of the
      -- animation cycle.
      frameNumber     = frame `mod` totalFrames

      -- Calculate frame number in a cycle from which stage 3 starts
      outStartFrame   = inFrameCount + delayFrameCount

      -- Find out, on which of the three stages we are:
      --
      -- For 1st & 3rd stages, calculate frame number relative to the first
      -- frame of each stage.
      yShift =
        if | frameNumber <= inFrameCount
             -- For the first stage, `relativeFrameNumber` is just `frameNumber` -
             -- - we are starting from 0.
             -> renderFade (slider ^.fadeIn) frameNumber inFrameCount
           | frameNumber < outStartFrame
             -> 0
           | otherwise
             -- For the third stage, `relativeFrameNumber` equals to how many frames
             -- are remaining
             -> renderFade (slider ^. fadeOut) (totalFrames - frameNumber) outFrameCount
  in
    ASTProp (P (XY (0, yShift))) ast


renderFade
  :: Fade
  -> Int
  -- ^ Number of the current frame
  -> Int
  -- ^ Total frame count in a stage
  -> Int
renderFade fade relativeFrameNumber frameCount =
  let height = fade ^. fadePixelHeight
      dk = direction (fade ^. fadeDirection)
  in
    dk * (height - height * relativeFrameNumber `div` frameCount)


direction :: VerticalDirection -> Int
direction VUp = -1
direction _   = 1