{- |
Functions that cope both with plain and non-empty structures.
-}
module Data.NonEmpty.Mixed (
   module Data.NonEmpty.Mixed,
   Priv.appendRight) where

import qualified Data.NonEmpty.Class as C
import qualified Data.NonEmptyPrivate as Priv
import qualified Data.NonEmpty as NonEmpty
import Data.Foldable (Foldable, foldr, )

import Prelude hiding (foldr, )


groupBy ::
   (Foldable f) =>
   (a -> a -> Bool) -> f a -> [NonEmpty.T [] a]
groupBy p =
   foldr
      (\x0 yt ->
         let (xr,yr) =
               case yt of
                  NonEmpty.Cons x1 xs : ys ->
                     if p x0 x1
                       then (x1:xs,ys)
                       else ([],yt)
                  [] -> ([],yt)
         in  NonEmpty.Cons x0 xr : yr)
      []

segmentBefore ::
   (Foldable f) =>
   (a -> Bool) -> f a -> ([a], [NonEmpty.T [] a])
segmentBefore p =
   foldr
      (\ x ys ->
         if p x
           then ([], NonEmpty.Cons x (fst ys) : snd ys)
           else (x : fst ys, snd ys))
      ([],[])

scanl :: (a -> b -> a) -> a -> [b] -> NonEmpty.T [] a
scanl f =
   let go a bt =
          NonEmpty.Cons a $
          case bt of
             [] -> []
             b:bs -> NonEmpty.flatten $ go (f a b) bs
   in  go

{-
Fusable and generic, but not as lazy as 'scanl'.
-}
genericScanl ::
   (Foldable f) =>
   (a -> b -> a) -> a -> f b -> NonEmpty.T [] a
genericScanl f a0 xs =
   NonEmpty.force $
   foldr
      (\ b go a ->
          NonEmpty.Cons a $ NonEmpty.flatten $ go $ f a b)
      (\ a -> NonEmpty.Cons a [])
      xs
      a0


insertBy ::
   (C.Sort f) =>
   (a -> a -> Ordering) -> a -> f a -> NonEmpty.T f a
insertBy f y xs = uncurry NonEmpty.Cons $ C.insertBy f y xs

insert :: (Ord a, C.Sort f) => a -> f a -> NonEmpty.T f a
insert = insertBy compare


infixl 5 `appendLeft`

appendLeft ::
   (C.Append f, C.View f, C.Cons f) =>
   f a -> NonEmpty.T f a -> NonEmpty.T f a
appendLeft xt yt =
   NonEmpty.force $
   case C.viewL xt of
      Nothing -> yt
      Just (x,xs) -> NonEmpty.Cons x $ C.append xs $ NonEmpty.flatten yt

tails ::
   (C.View f, C.Empty f) =>
   f a -> NonEmpty.T [] (f a)
tails xt =
   NonEmpty.force $
   case C.viewL xt of
      Nothing -> NonEmpty.Cons C.empty []
      Just (_, xs) -> NonEmpty.cons xt $ tails xs

inits ::
   (C.View f, C.Cons f, C.Empty f) =>
   f a -> NonEmpty.T [] (f a)
inits xt =
   NonEmpty.Cons C.empty $
   case C.viewL xt of
      Nothing -> []
      Just (x,xs) -> map (C.cons x) $ NonEmpty.flatten $ inits xs