-- | Utilities for working with 'Data.Text.Text'
module Mikan.Utils.Text
  ( -- * Substrings
    breakAfter
  , spanEnd
  ) where

import Prelude
  ( Char, Bool, Int
  , Ord(..), Num(..)
  , fromIntegral, divMod
  , Applicative(..), Monad(..)
  , otherwise
  )
import Prelude qualified

import Control.Monad.ST

import Data.Primitive.ByteArray
import Data.Text
import Data.Text.Internal
import Data.Text.Unsafe
import Data.Word

--------------------------------------------------------------------------------
-- Substrings

{-# INLINE breakAfter #-}
-- | Breaks a bit of text just /after/ a character satisfying
-- the predicate is found.
--
-- ==== __Examples__
--
-- >>> breakAfter (== 'b') "aaaabbbb"
-- ("aaaab","bbb")
--
-- >>> breakAfter (== 'a') "aaaabbbb"
-- ("a","aaabbbb")
--
-- >>> breakAfter (== 'c') "aaaabbbb"
-- ("aaaabbbb","")
breakAfter :: (Char -> Bool) -> Text -> (Text, Text)
breakAfter :: (Char -> Bool) -> Text -> (Text, Text)
breakAfter Char -> Bool
p t :: Text
t@(Text Array
arr Int
off Int
len) = (Text
hd, Text
tl) where
  !k :: Int
k = Int -> Int
loop Int
0
  !hd :: Text
hd = Array -> Int -> Int -> Text
Text Array
arr Int
off Int
k
  !tl :: Text
tl = Array -> Int -> Int -> Text
Text Array
arr (Int
off Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
k) (Int
len Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
k)

  loop :: Int -> Int
loop !Int
i
    | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
len = case Array -> Int -> Iter
iterArray Array
arr (Int
off Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
i) of
      Iter Char
c Int
d
        | Char -> Bool
p Char
c -> Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
        | Bool
otherwise -> Int -> Int
loop (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
d)
    | Bool
otherwise = Int
i

{-# INLINE spanEnd #-}
-- | 'spanEnd', applied to a predicate @p@,
-- a text @t@, returns a pair @(t1, t2)@ where @t2@ is the longest suffix of
-- @t@ whose elements satisfy @p@, and @t1@ is the remainder of the text.
spanEnd :: (Char -> Bool) -> Text -> (Text, Text)
spanEnd :: (Char -> Bool) -> Text -> (Text, Text)
spanEnd Char -> Bool
p t :: Text
t@(Text Array
arr Int
off Int
len) = Int -> (Text, Text)
loop (Int
len Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) where
  loop :: Int -> (Text, Text)
loop !Int
i
    | Int
0 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
i = case Array -> Int -> Iter
reverseIterArray Array
arr (Int
off Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
i) of
      Iter Char
c Int
l
        | Char -> Bool
p Char
c -> Int -> (Text, Text)
loop (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
l)
        | Bool
otherwise -> (Array -> Int -> Int -> Text
text Array
arr Int
off (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1), Array -> Int -> Int -> Text
text Array
arr (Int
off Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (Int
len Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1))
   | Bool
otherwise = (Text
empty, Text
t)