    -- |
    -- Module      : Foundation.Collection.List
    -- License     : BSD-style
    -- Maintainer  : Vincent Hanquez <vincent@snarc.org>
    -- Stability   : experimental
    -- Portability : portable
    --
    module Foundation.Collection.List
        ( wordsWhen
        , revTake
        , revDrop
        , revSplitAt
        , breakEnd
        , uncons
        , unsnoc
        ) where

    import qualified Data.List
    import           Data.Tuple (swap)
    import           Basement.Compat.Base
    import           Foundation.Numerical

    -- | Simple helper to split a list repeatly when the predicate match
    wordsWhen     :: (x -> Bool) -> [x] -> [[x]]
    wordsWhen _ [] = [[]]
    wordsWhen p is = loop is
      where
        loop s =
            let (w, s') = Data.List.break p s
             in case s' of
                    []   -> [w]
                    _:xs -> w : loop xs

    revTake :: Int -> [a] -> [a]
    revTake n l = Data.List.drop (len - n) l
      where
        len = Data.List.length l

    revDrop :: Int -> [a] -> [a]
    revDrop n l = Data.List.take (len - n) l
      where
        len = Data.List.length l

    revSplitAt :: Int -> [a] -> ([a],[a])
    revSplitAt n l = swap $ Data.List.splitAt (len - n) l
      where
        len = Data.List.length l

    breakEnd :: (a -> Bool) -> [a] -> ([a], [a])
    breakEnd predicate l =
        let (l1,l2) = Data.List.break predicate (Data.List.reverse l)
         in if Data.List.null l2 then (l, []) else (Data.List.reverse l2, Data.List.reverse l1)

    uncons :: [a] -> Maybe (a, [a])
    uncons []     = Nothing
    uncons (x:xs) = Just (x,xs)

    unsnoc :: [a] -> Maybe ([a], a)
    unsnoc []       = Nothing
    unsnoc [x]      = Just ([], x)
    unsnoc [x,y]    = Just ([x], y)
    unsnoc (x:xs@(_:_)) = Just $ loop [x] xs
      where
        loop acc [y]    = (Data.List.reverse acc, y)
        loop acc (y:ys) = loop (y:acc) ys
        loop _   _      = error "impossible"