{-# LANGUAGE BangPatterns          #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies          #-}
{-# LANGUAGE UndecidableInstances  #-}

module Plots.Util
  ( pathFromVertices
  , minMaxOf
  , enumFromToN
  , whenever

    -- * State helpers
  , (&=)
  , (&~~)
  ) where

import           Control.Lens
import           Control.Monad.State
import           Data.Bool

import           Diagrams.Prelude           hiding (diff)

-- | Similar to '(%=)' but takes a state modification instead of a
--   function.
(&=) :: MonadState s m => ASetter' s b -> State b a -> m ()
l &= s = l %= execState s
infix 3 &=

-- | Similar to '(&~)' but works with 'StateT' and returns it in @m@.
(&~~) :: Monad m => s -> StateT s m a -> m s
l &~~ s = execStateT s l
infix 1 &~~

-- | @enumFromToN a b n@ calculates a list from @a@ to @b@ in @n@ steps.
enumFromToN :: Fractional n => n -> n -> Int -> [n]
enumFromToN a b n = go n a
  where
    go !i !x | i < 1     = [x]
             | otherwise = x : go (i - 1) (x + diff)
    diff = (b - a) / fromIntegral n

-- | Apply a function if the predicate is true.
whenever :: Bool -> (a -> a) -> a -> a
whenever b f = bool id f b

------------------------------------------------------------------------
-- Diagrams
------------------------------------------------------------------------

-- | Type specialised version of 'fromVertices'.
pathFromVertices :: (Metric v, OrderedField n) => [Point v n] -> Path v n
pathFromVertices = fromVertices
{-# INLINE pathFromVertices #-}

-- | Minmax of a getter in the form @V2 min max@. Returns @(V2
--   (-Infinity) Infinity)@ for empty folds.
minMaxOf :: (Fractional a, Ord a) => Getting (Endo (Endo (V2 a))) s a -> s -> (a,a)
minMaxOf l = foldlOf' l (\(V2 mn mx) a -> V2 (min mn a) (max mx a)) (V2 (1/0) (-1/0))
          <&> \(V2 x y) -> (x,y)
      -- (\acc a -> acc <**> V2 min max ?? a)
-- V2 is used instead of a tuple because V2 is strict.
{-# INLINE minMaxOf #-}