module DzenDhall.Animation.Marquee where

import           DzenDhall.AST
import           DzenDhall.Config
import           DzenDhall.Data
import qualified DzenDhall.Extra as Extra

import           Data.Function (fix)
import           Lens.Micro

run :: Int -> Marquee -> AST -> Int -> AST
run fontWidth settings ast frameCounter =
  let framesPerChar = settings ^. mqFramesPerChar
      desiredWidth  = settings ^. mqWidth
      shouldWrap    = settings ^. mqShouldWrap

      realWidth     = astWidth ast
      difference    = realWidth - desiredWidth in

  if | difference <= 0 && not shouldWrap ->
         -- Pad AST
         let padding = ASTText (Extra.spaces (- difference)) in
           ASTs ast padding

     | otherwise ->
         -- Select a part of an "infinite" version of AST.
         let shifted = snd $
               DzenDhall.AST.split
               -- `framesPerChar` is checked for zero when marshalling.
               ((frameCounter `div` framesPerChar) `mod` realWidth)
               (fix (ASTs ast))
             trimmed = fst $
               DzenDhall.AST.split desiredWidth shifted in
           if | framesPerChar == 1 -> trimmed
              | otherwise ->
                let pxShift = calculatePxShift frameCounter fontWidth framesPerChar in
                  addPxShift pxShift trimmed


-- | Calculate shift in pixels.
calculatePxShift
  :: Int
  -- ^ Number of current frame
  -> Int
  -- ^ Character width
  -> Int
  -- ^ How many frames per character?
  -> Int
  -- ^ Shift in pixels
calculatePxShift frameCounter fontWidth framesPerChar =
  let shift = frameCounter `mod` framesPerChar in
    fontWidth - (fontWidth * shift) `div` framesPerChar


-- | Add sub-character shift to the AST and compensate it
addPxShift
  :: Int
  -- ^ Shift in pixels
  -> AST
  -> AST
addPxShift pxShift =
  ASTProp (P (XY (pxShift, 0)))