-- | Custom prelude to avoid many pitfalls of the standard.
module Prelude
  ( -- * Function composition
    (.)
  , id
  -- * 'Applicative' operations
  , for
  , for_
  , sequence
  , sequence_
  , traverse_
  , replicateA
  , replicateA_
  -- * List operations
  , (!!)
  , map
  , head
  , ordNub
  -- * Time and date
  , UTCTime(..)
  , NominalDiffTime
  , addUTCTime
  , diffUTCTime
  , getCurrentTime
  -- * 'String' operations
  , stringToText
  , stringToLText
  -- * Error reporting
  , error
  -- * Re-exports
  , module BasicPrelude
  , module Control.Monad.Catch
  ) where

import BasicPrelude hiding
  ( -- Prefer type-specialised versions
    (.), id, map
  -- Prefer to avoid, or have a WARNING pragma
  , error, terror, undefined, fail, head, last, (!!)
  -- Prefer '<>'
  , (++)
  -- Avoid deprecated
  , empty, fpToText, fpFromText, fpToString
  -- Prefer 'Applicative' versions
  , forM, forM_, mapM, mapM_, sequence, sequence_, return, replicateM
  , replicateM_, liftM, liftM2, liftM3, liftM4, liftM5, ap
  -- Prefer 'Alternative'
  , MonadPlus(..), msum, mfilter
  -- Prefer "Control.Monad.Catch" versions
  , throwIO, try, tryJust, catch, catchJust, catchIOError, handle, handleJust
  , bracket, bracket_, bracketOnError, onException, finally, mask, mask_
  , uninterruptibleMask, uninterruptibleMask_
  -- Name conflicts with things used more frequently
  , Hashable(..), delete, insert, isJust, isNothing, on
  )
import qualified BasicPrelude as P
import Control.Monad.Catch
import Data.Foldable (for_, traverse_)
import qualified Data.Foldable as F
import qualified Data.Set as S
import qualified Data.Text as T
import qualified Data.Text.Lazy as LT
import Data.Time.Clock (UTCTime(..), NominalDiffTime, addUTCTime, diffUTCTime)
import qualified Data.Time.Clock as C
import Data.Traversable (for)
import qualified Data.Traversable as T

-- HLint has some problems with the renamed functions, as it's a
-- simple pattern matcher.
{-# ANN module ("HLint: ignore Use replicateM"  :: String) #-}
{-# ANN module ("HLint: ignore Use replicateM_" :: String) #-}

-- | Function composition.
(.) :: (b -> c) -> (a -> b) -> a -> c
{-# INLINE (.) #-}
(.) = (P..)

-- | 'map' @f xs@ is the list obtained by applying @f@ to each element
-- of @xs@.
map :: (a -> b) -> [a] -> [b]
{-# INLINE map #-}
map = P.map

-- | Identity function.
id :: a -> a
{-# INLINE id #-}
id = P.id

-- | 'error' stops execution and displays an error message.
error :: Text -> a
{-# INLINE  error #-}
{-# WARNING error "Use of error." #-}
error = P.terror

-- | Evaluate each action in structure from left to right, and collect
-- the results. For a version that ignores the results see
-- 'sequence_'.
sequence :: (Traversable t, Applicative f )=> t (f a) -> f (t a)
{-# INLINE sequence #-}
sequence = T.sequenceA

-- | Evaluate each action in the structure from left to right, and
-- ignore the results. For a version that doesn't ignore the results
-- see 'sequence'.
sequence_ :: (Foldable t, Applicative f) => t (f a) -> f ()
{-# INLINE sequence_ #-}
sequence_ = F.sequenceA_

-- | Extract the first element of a list, which must be non-empty.
head :: [a] -> a
{-# INLINE  head #-}
{-# WARNING head "Use of head. Consider pattern matching or 'uncons' instead."
  #-}
head = P.head

-- | List index (subscript) operator, starting from 0.
(!!) :: Integral i => [a] -> i -> a
{-# INLINE  (!!) #-}
{-# WARNING (!!) "Use of !!. Are lists the right data structure here?" #-}
(!!) = P.genericIndex

-- | 'replicateA' @n act@ performs the action @n@ times, gathering the
-- results.
replicateA :: Applicative f => Int -> f a -> f [a]
{-# INLINE replicateA #-}
replicateA i = sequence . replicate i

-- | Like 'replicateA', but discards the result.
replicateA_ :: Applicative f => Int -> f a -> f ()
{-# INLINE replicateA_ #-}
replicateA_ i = sequence_ . replicate i

-- | Fast 'nub' using 'Set' operations.
ordNub :: Ord a => [a] -> [a]
{-# INLINE ordNub #-}
ordNub = S.toList . S.fromList

-- | Get the current UTC time from the system clock.
getCurrentTime :: MonadIO m => m UTCTime
{-# INLINE getCurrentTime #-}
getCurrentTime = liftIO C.getCurrentTime

-- | Convert a 'String' to a 'Text'.
stringToText :: String -> Text
{-# INLINE stringToText #-}
stringToText = T.pack

-- | Convert a 'String' to a 'LText'.
stringToLText :: String -> LText
{-# INLINE stringToLText #-}
stringToLText = LT.pack
