module Data.JSString.Internal.Fusion ( -- * Types
                                       Stream(..)
                                     , Step(..)

                                       -- * Creation and elimination
                                     , stream
                                     , unstream
                                     , reverseStream

                                     , length

                                       -- * Transformations
                                     , reverse

                                       -- * Construction
                                       -- ** Scans
                                     , reverseScanr

                                       -- ** Accumulating maps
                                     , mapAccumL

                                       -- ** Generation and unfolding
                                     , unfoldrN

                                       -- * Indexing
                                     , index
                                     , findIndex
                                     , countChar
                                     ) where

import Prelude hiding (length, reverse)

import Data.JSString.Internal.Type

import qualified Data.Text.Internal.Fusion as T
import Data.Text.Internal.Fusion (Stream(..), Step(..))

-- | /O(n)/ Convert a 'JSString' into a 'Stream Char'.
stream :: JSString -> Stream Char
stream (JSString t) = T.stream t
{-# INLINE [0] stream #-}

-- | /O(n)/ Convert a 'JSString' into a 'Stream Char', but iterate
-- backwards.
reverseStream :: JSString -> Stream Char
reverseStream (JSString x) = T.reverseStream x
{-# INLINE [0] reverseStream #-}

-- | /O(n)/ Convert a 'Stream Char' into a 'JSString'.
unstream :: Stream Char -> JSString
unstream strm = JSString $ T.unstream strm
{-# INLINE [0] unstream #-}
{-# RULES "STREAM stream/unstream fusion" forall s. stream (unstream s) = s #-}


-- ----------------------------------------------------------------------------
-- * Basic stream functions

length :: Stream Char -> Int
length = T.length
{-# INLINE[0] length #-}

-- | /O(n)/ Reverse the characters of a string.
reverse :: Stream Char -> JSString
reverse = JSString . T.reverse
{-# INLINE [0] reverse #-}

-- | /O(n)/ Perform the equivalent of 'scanr' over a list, only with
-- the input and result reversed.
reverseScanr :: (Char -> Char -> Char) -> Char -> Stream Char -> Stream Char
reverseScanr = T.reverseScanr
{-# INLINE reverseScanr #-}

-- | /O(n)/ Like 'unfoldr', 'unfoldrN' builds a stream from a seed
-- value. However, the length of the result is limited by the
-- first argument to 'unfoldrN'. This function is more efficient than
-- 'unfoldr' when the length of the result is known.
unfoldrN :: Int -> (a -> Maybe (Char,a)) -> a -> Stream Char
unfoldrN = T.unfoldrN
{-# INLINE [0] unfoldrN #-}

-------------------------------------------------------------------------------
-- ** Indexing streams

-- | /O(n)/ stream index (subscript) operator, starting from 0.
index :: Stream Char -> Int -> Char
index = T.index
{-# INLINE [0] index #-}

-- | The 'findIndex' function takes a predicate and a stream and
-- returns the index of the first element in the stream
-- satisfying the predicate.
findIndex :: (Char -> Bool) -> Stream Char -> Maybe Int
findIndex = T.findIndex
{-# INLINE [0] findIndex #-}

-- | /O(n)/ The 'count' function returns the number of times the query
-- element appears in the given stream.
countChar :: Char -> Stream Char -> Int
countChar = T.countChar
{-# INLINE [0] countChar #-}

-- | /O(n)/ Like a combination of 'map' and 'foldl''. Applies a
-- function to each element of a 'Text', passing an accumulating
-- parameter from left to right, and returns a final 'JSString'.
mapAccumL :: (a -> Char -> (a, Char)) -> a -> Stream Char -> (a, JSString)
mapAccumL f z strm = JSString <$> T.mapAccumL f z strm
{-# INLINE [0] mapAccumL #-}