{-# LANGUAGE TypeFamilies           #-}
{-# LANGUAGE TypeInType             #-}
{-# LANGUAGE TypeOperators          #-}
{-# LANGUAGE UndecidableInstances   #-}
{-# OPTIONS_GHC -Wall                       #-}
{-# OPTIONS_GHC -Werror=incomplete-patterns #-}

{-|
Module      : Fcf.Data.Text
Description : Type-level Text data structure with methods
Copyright   : (c) gspia 2020-
License     : BSD
Maintainer  : gspia

= Fcf.Data.Text

We mimick Data.Text but on type level. The current internal representation is based
on type level lists.  The current (as of early 2023) implementation of this
Fcf.Data.Text will be deprecated and replaced with the contents of Fcf.Data.NewText
later 2023 as newer version GHC become more widespread.

The old version working with 9.0.x or less will be kept at Fcf.Data.OldText for
some time.  Similarly, the module Fcf.Data.NewText contains the functions
and definitions for better Text type, which will be taken into use after some time.

-}

--------------------------------------------------------------------------------

module Fcf.Data.Text
    ( Text (..)

    -- * Creation
    , Empty
    , Singleton
    , FromList
    , FromSymbolList
    , ToList
    , ToSymbol
    , ToSymbolList

    -- * Basic Interface
    , Null
    , Length
    , Append
    , Cons
    , Snoc
    , Uncons
    , Unsnoc
    , Head
    , Tail
    , Init
    , CompareLength

    -- * Transformation
    , FMap
    , Intercalate
    , Intersperse
    , Reverse
    , Replace

    -- * Special Folds
    , Concat
    , FConcatMap
    , Any
    , All

    -- * Substrings
    , Take
    , TakeEnd
    , Drop
    , DropEnd
    , TakeWhile
    , TakeWhileEnd
    , DropWhile
    , DropWhileEnd
    , DropAround
    , Strip

    -- * Breaking etc
    , SplitOn
    , Split
    , Lines
    , Words
    , Unlines
    , Unwords

    -- * Predicates
    , IsPrefixOf
    , IsSuffixOf
    , IsInfixOf
    )
  where

import           GHC.TypeLits (Symbol)
import qualified GHC.TypeLits as TL

import           Fcf ( If, Eval, Exp, type (=<<), type (@@)
                     , Flip, Pure)
import qualified Fcf.Class.Foldable as F (All, Any)
import           Fcf.Data.List ( type (++) )
import qualified Fcf.Data.List as F
    ( Length, Init, Reverse, Take, Drop, TakeWhile, DropWhile
    , Intercalate, Intersperse, IsPrefixOf, IsSuffixOf, IsInfixOf, Snoc)

import qualified Fcf.Class.Functor as F ( FMap )


import           Fcf.Data.Nat (Nat)
import qualified Fcf.Data.Text.Internal as T
import           Fcf.Alg.Other ( PairMaybeToMaybePair )
import           Fcf.Alg.Morphism (First,Second)
import qualified Fcf.Alg.Symbol as S
import           Fcf.Alg.Nat (type (==))

--------------------------------------------------------------------------------


-- For the doctests:

-- $setup
-- >>> import           Fcf (type (<=<), Not)

--------------------------------------------------------------------------------

-- | 'Text' is a data structure, that is, a list to hold type-level symbols of
-- length one.
newtype Text = Text Symbol

--------------------------------------------------------------------------------

-- | Empty
--
-- === __Example__
--
-- >>> :kind! (Eval Empty :: Text)
-- (Eval Empty :: Text) :: Text
-- = 'Text ""
--
-- See also the other examples in this module.
data Empty :: Exp Text
type instance Eval Empty = 'Text ""

-- | Singleton
--
-- === __Example__
--
-- >>> :kind! Eval (Singleton "a")
-- Eval (Singleton "a") :: Text
-- = 'Text "a"
data Singleton :: Symbol -> Exp Text
type instance Eval (Singleton s) = 'Text s



-- | Use FromList to construct a Text from type-level list.
--
-- === __Example__
--
-- :kind! Eval (FromSymbolList '["h", "e", "l", "l", "u", "r", "e", "i"])
-- Eval (FromSymbolList '["h", "e", "l", "l", "u", "r", "e", "i"]) :: Text
-- = 'Text "hellurei"
data FromSymbolList :: [Symbol] -> Exp Text
type instance Eval (FromSymbolList sym) =  'Text (T.ToSymbol2 @@ sym)


-- hmm, this is also a Monoid, so we should be able to use Concat
data FromList :: [Text] -> Exp Text
type instance Eval (FromList txt) =
    'Text (T.ToSymbol2 @@ Eval (F.FMap ToSymbol txt))

-- | Get the type-level list out of the 'Text'.
--
-- === __Example__
--
-- >>> :kind! Eval (ToSymbolList =<< FromSymbolList '["a", "b"])
-- Eval (ToSymbolList =<< FromSymbolList '["a", "b"]) :: [Symbol]
-- = '["a", "b"]
data ToSymbolList :: Text -> Exp [Symbol]
type instance Eval (ToSymbolList ('Text sym)) = T.ToListA @@ sym


-- | Split 'Text' to single character 'Text' list.
--
-- === __Example__
--
-- >>> :kind! Eval (ToList =<< FromSymbolList '["a", "b"])
-- Eval (ToList =<< FromSymbolList '["a", "b"]) :: [Text]
-- = '[ 'Text "a", 'Text "b"]
data ToList :: Text -> Exp [Text]
type instance Eval (ToList txt) = Eval (F.FMap Singleton =<< ToSymbolList txt)


-- | ToSymbol
--
-- === __Example__
--
-- >>> :kind! Eval (ToSymbol =<< FromSymbolList '["w", "o", "r", "d"])
-- Eval (ToSymbol =<< FromSymbolList '["w", "o", "r", "d"]) :: Symbol
-- = "word"
data ToSymbol :: Text -> Exp Symbol
type instance Eval (ToSymbol ('Text sym)) = sym



-- | Null
--
-- === __Example__
--
-- >>> :kind! Eval (Null ('Text "ab"))
-- Eval (Null ('Text "ab")) :: Bool
-- = 'False
--
-- >>> :kind! Eval (Null =<< Empty)
-- Eval (Null =<< Empty) :: Bool
-- = 'True
data Null :: Text -> Exp Bool
type instance Eval (Null txt) = Eval
    (If (Eval (Eval (Length txt) == 0))
        (Pure 'True)
        (Pure 'False)
    )


-- | Length
--
-- === __Example__
--
-- >>> :kind! Eval (Length =<< Singleton "ab")
-- Eval (Length =<< Singleton "ab") :: TL.Natural
-- = 2
data Length :: Text -> Exp Nat
type instance Eval (Length ('Text sym)) = Eval (F.Length (T.ToList sym))


-- | Add a symbol to the beginning of a type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Cons "h" ('Text "aamu"))
-- Eval (Cons "h" ('Text "aamu")) :: Text
-- = 'Text "haamu"
data Cons :: Symbol -> Text -> Exp Text
type instance Eval (Cons s ('Text sym)) = 'Text (TL.AppendSymbol s sym)

-- | Add a symbol to the end of a type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Snoc ('Text "aam") "u")
-- Eval (Snoc ('Text "aam") "u") :: Text
-- = 'Text "aamu"
data Snoc :: Text -> Symbol -> Exp Text
type instance Eval (Snoc ('Text sym) s) = 'Text (TL.AppendSymbol sym s)

-- | Append two type-level texts.
--
-- === __Example__
--
-- >>> :kind! Eval (Append ('Text "aa") ('Text "mu"))
-- Eval (Append ('Text "aa") ('Text "mu")) :: Text
-- = 'Text "aamu"
data Append :: Text -> Text -> Exp Text
type instance Eval (Append ('Text s1) ('Text s2)) = 'Text (TL.AppendSymbol s1 s2)


-- | Get the first symbol from type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Uncons ('Text "haamu"))
-- Eval (Uncons ('Text "haamu")) :: Maybe (Symbol, Text)
-- = 'Just '("h", 'Text "aamu")
--
-- >>> :kind! Eval (Uncons ('Text ""))
-- Eval (Uncons ('Text "")) :: Maybe (Symbol, Text)
-- = 'Nothing
data Uncons :: Text -> Exp (Maybe (TL.Symbol, Text))
type instance Eval (Uncons txt) =
    Eval (PairMaybeToMaybePair '( Eval (Head txt), Eval (Tail txt) ))




-- | Get the last symbol from type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Unsnoc ('Text "aamun"))
-- Eval (Unsnoc ('Text "aamun")) :: Maybe (Symbol, Text)
-- = 'Just '("n", 'Text "aamu")
--
-- >>> :kind! Eval (Unsnoc ('Text ""))
-- Eval (Unsnoc ('Text "")) :: Maybe (Symbol, Text)
-- = 'Nothing
data Unsnoc :: Text -> Exp (Maybe (Symbol, Text))
type instance Eval (Unsnoc txt) = 
    Eval (F.FMap (Second Reverse) =<< Uncons =<< Reverse txt)


-- | Get the first symbol of type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Head ('Text "aamu"))
-- Eval (Head ('Text "aamu")) :: Maybe Symbol
-- = 'Just "a"
--
-- >>> :kind! Eval (Head ('Text ""))
-- Eval (Head ('Text "")) :: Maybe Symbol
-- = 'Nothing
data Head :: Text -> Exp (Maybe Symbol)
type instance Eval (Head ('Text sym)) = Eval
    (If (Eval (Eval (Length ('Text sym)) == 0))
        (Pure 'Nothing)
        (Pure ('Just (T.Head1 sym (TL.CmpSymbol sym "\128"))))
    )

-- | Get the tail of a type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Tail ('Text "haamu"))
-- Eval (Tail ('Text "haamu")) :: Maybe Text
-- = 'Just ('Text "aamu")
--
-- >>> :kind! Eval (Tail ('Text ""))
-- Eval (Tail ('Text "")) :: Maybe Text
-- = 'Nothing
data Tail :: Text -> Exp (Maybe Text)
type instance Eval (Tail ('Text sym)) = Eval (F.FMap Singleton =<< T.Uncons sym)

-- | Take all except the last symbol from type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Init ('Text "aamun"))
-- Eval (Init ('Text "aamun")) :: Maybe Text
-- = 'Just ('Text "aamu")
--
-- >>> :kind! Eval (Init ('Text ""))
-- Eval (Init ('Text "")) :: Maybe Text
-- = 'Nothing
data Init :: Text -> Exp (Maybe Text)
type instance Eval (Init txt) = Eval (F.FMap FromList =<< F.Init =<< ToList txt)


-- | Compare the length of type-level text to given Nat and give
-- the Ordering.
--
-- === __Example__
--
-- >>> :kind! Eval (CompareLength ('Text "aamu") 3)
-- Eval (CompareLength ('Text "aamu") 3) :: Ordering
-- = 'GT
data CompareLength :: Text -> Nat -> Exp Ordering
type instance Eval (CompareLength txt n) = TL.CmpNat (Length @@ txt) n



-- | FMap for type-level text.
--
-- === __Example__
--
-- >>> :{
-- data IsIsymb :: Symbol -> Exp Bool
-- type instance Eval (IsIsymb s) = Eval ("i" S.== s)
-- data Isymb2e :: Symbol -> Exp Symbol
-- type instance Eval (Isymb2e s) = Eval
--     (If (IsIsymb @@ s)
--         (Pure "e")
--         (Pure s)
--     )
-- :}
--
-- >>> :kind! Eval (FMap Isymb2e ('Text "imu"))
-- Eval (FMap Isymb2e ('Text "imu")) :: Text
-- = 'Text "emu"
data FMap :: (Symbol -> Exp Symbol) -> Text -> Exp Text
type instance Eval (FMap f txt) = Eval (FromSymbolList =<< F.FMap f =<< ToSymbolList txt)

data FMapT :: (Text -> Exp Text) -> Text -> Exp Text
type instance Eval (FMapT f txt) = Eval (FromList =<< F.FMap f =<< ToList txt)


-- | Intercalate for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Intercalate ('Text " & ") ('[ 'Text "aamu", 'Text "valo"]))
-- Eval (Intercalate ('Text " & ") ('[ 'Text "aamu", 'Text "valo"])) :: Text
-- = 'Text "aamu & valo"
data Intercalate :: Text -> [Text] -> Exp Text
type instance Eval (Intercalate txt txts) = 
    Eval (FromList =<< F.Intercalate '[txt] =<< F.FMap ToList txts)


-- | Intersperse for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Intersperse "." ('Text "aamu"))
-- Eval (Intersperse "." ('Text "aamu")) :: Text
-- = 'Text "a.a.m.u"
data Intersperse :: Symbol -> Text -> Exp Text
type instance Eval (Intersperse s ('Text txt)) =
    Eval (FromSymbolList =<< F.Intersperse s (T.ToList txt))

-- | Reverse for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Reverse ('Text "aamu"))
-- Eval (Reverse ('Text "aamu")) :: Text
-- = 'Text "umaa"
--
-- >>> :kind! Eval (Reverse =<< Reverse ('Text "aamu"))
-- Eval (Reverse =<< Reverse ('Text "aamu")) :: Text
-- = 'Text "aamu"
data Reverse :: Text -> Exp Text
type instance Eval (Reverse txt) =  Eval (FromList =<< F.Reverse =<< ToList txt)

-- | Replace for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Replace ('Text "tu") ('Text "la") ('Text "tuututtaa"))
-- Eval (Replace ('Text "tu") ('Text "la") ('Text "tuututtaa")) :: Text
-- = 'Text "laulattaa"
data Replace :: Text -> Text -> Text -> Exp Text
type instance Eval (Replace orig new txt) =
    Eval (Intercalate new =<< SplitOn orig txt)


-- | Concat for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Concat '[ 'Text "la", 'Text "kana"])
-- Eval (Concat '[ 'Text "la", 'Text "kana"]) :: Text
-- = 'Text "lakana"
data Concat :: [Text] -> Exp Text
type instance Eval (Concat lst) = 
    'Text (T.ToSymbol2 @@ Eval (F.FMap ToSymbol lst))



-- | FConcatMap for type-level text.
--
-- === __Example__
--
-- >>> :{
-- data IsIsymb :: Symbol -> Exp Bool
-- type instance Eval (IsIsymb s) = Eval ("i" S.== s)
-- data Isymb2aa :: Symbol -> Exp Text
-- type instance Eval (Isymb2aa s) = Eval
--     (If (IsIsymb @@ s)
--         (Pure ('Text "aa"))
--         (Pure ('Text s))
--     )
-- :}
--
-- >>> :kind! Eval (FConcatMap Isymb2aa ('Text "imu ih"))
-- Eval (FConcatMap Isymb2aa ('Text "imu ih")) :: Text
-- = 'Text "aamu aah"
data FConcatMap :: (Symbol -> Exp Text) -> Text -> Exp Text
type instance Eval (FConcatMap f ('Text lst)) = Eval (Concat =<< F.FMap f (T.ToList lst))

-- | Any for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Any S.IsDigit ('Text "aamu1"))
-- Eval (Any S.IsDigit ('Text "aamu1")) :: Bool
-- = 'True
--
-- >>> :kind! Eval (Any S.IsDigit ('Text "aamu"))
-- Eval (Any S.IsDigit ('Text "aamu")) :: Bool
-- = 'False
data Any :: (Symbol -> Exp Bool) -> Text -> Exp Bool
type instance Eval (Any f ('Text sym)) = Eval (F.Any f (T.ToList sym))


-- | All for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (All S.IsDigit ('Text "aamu1"))
-- Eval (All S.IsDigit ('Text "aamu1")) :: Bool
-- = 'False
--
-- >>> :kind! Eval (All S.IsDigit ('Text "321"))
-- Eval (All S.IsDigit ('Text "321")) :: Bool
-- = 'True
data All :: (Symbol -> Exp Bool) -> Text -> Exp Bool
type instance Eval (All f ('Text lst)) = Eval (F.All f (T.ToList lst))





-- | Take for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Take 4 ('Text "aamun"))
-- Eval (Take 4 ('Text "aamun")) :: Text
-- = 'Text "aamu"
data Take :: Nat -> Text -> Exp Text
type instance Eval (Take n ('Text lst)) = Eval (FromSymbolList =<< F.Take n (T.ToList lst))


-- | TakeEnd for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (TakeEnd 4 ('Text "haamu"))
-- Eval (TakeEnd 4 ('Text "haamu")) :: Text
-- = 'Text "aamu"
data TakeEnd :: Nat -> Text -> Exp Text
type instance Eval (TakeEnd n ('Text lst)) =
    Eval (FromSymbolList =<< F.Reverse =<< F.Take n =<< F.Reverse (T.ToList lst))


-- | Drop for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Drop 2 ('Text "aamuna"))
-- Eval (Drop 2 ('Text "aamuna")) :: Text
-- = 'Text "muna"
data Drop :: Nat -> Text -> Exp Text
type instance Eval (Drop n ('Text lst)) = Eval (FromSymbolList =<< F.Drop n (T.ToList lst))

-- | DropEnd for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (DropEnd 2 ('Text "aamuna"))
-- Eval (DropEnd 2 ('Text "aamuna")) :: Text
-- = 'Text "aamu"
data DropEnd :: Nat -> Text -> Exp Text
type instance Eval (DropEnd n ('Text lst)) =
    Eval (FromSymbolList =<< F.Reverse =<< F.Drop n =<< F.Reverse (T.ToList lst))


-- | TakeWhile for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (TakeWhile (Not <=< S.IsDigit) ('Text "aamu12"))
-- Eval (TakeWhile (Not <=< S.IsDigit) ('Text "aamu12")) :: Text
-- = 'Text "aamu"
data TakeWhile :: (Symbol -> Exp Bool) -> Text -> Exp Text
type instance Eval (TakeWhile f ('Text lst)) = Eval (FromSymbolList =<< F.TakeWhile f (T.ToList lst))


-- | TakeWhileEnd for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (TakeWhileEnd (Not <=< S.IsDigit) ('Text "12aamu"))
-- Eval (TakeWhileEnd (Not <=< S.IsDigit) ('Text "12aamu")) :: Text
-- = 'Text "aamu"
data TakeWhileEnd :: (Symbol -> Exp Bool) -> Text -> Exp Text
type instance Eval (TakeWhileEnd f ('Text lst)) =
    Eval (FromSymbolList =<< F.Reverse =<< F.TakeWhile f =<< F.Reverse (T.ToList lst))


-- | DropWhile for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (DropWhile S.IsDigit ('Text "12aamu"))
-- Eval (DropWhile S.IsDigit ('Text "12aamu")) :: Text
-- = 'Text "aamu"
data DropWhile :: (Symbol -> Exp Bool) -> Text -> Exp Text
type instance Eval (DropWhile f ('Text lst)) = Eval (FromSymbolList =<< F.DropWhile f (T.ToList lst))


-- | DropWhileEnd for type-level text.
-- === __Example__
--
-- >>> :kind! Eval (DropWhileEnd S.IsDigit ('Text "aamu12"))
-- Eval (DropWhileEnd S.IsDigit ('Text "aamu12")) :: Text
-- = 'Text "aamu"
data DropWhileEnd :: (Symbol -> Exp Bool) -> Text -> Exp Text
type instance Eval (DropWhileEnd f ('Text lst)) =
    Eval (FromSymbolList =<< F.Reverse =<< F.DropWhile f =<< F.Reverse (T.ToList lst))


-- | DropAround for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (DropAround S.IsDigit ('Text "34aamu12"))
-- Eval (DropAround S.IsDigit ('Text "34aamu12")) :: Text
-- = 'Text "aamu"
data DropAround :: (Symbol -> Exp Bool) -> Text -> Exp Text
type instance Eval (DropAround f txt) = Eval (DropWhile f =<< DropWhileEnd f txt)



-- | Strip the space, newline and tab -symbols from the beginning and and
-- of type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Strip ('Text "  aamu \n"))
-- Eval (Strip ('Text "  aamu \n")) :: Text
-- = 'Text "aamu"
data Strip :: Text -> Exp Text
type instance Eval (Strip txt) = Eval (DropAround S.IsSpaceDelim txt)


-- | SplitOn for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (SplitOn ('Text "ab") ('Text "cdabfgabh"))
-- Eval (SplitOn ('Text "ab") ('Text "cdabfgabh")) :: [Text]
-- = '[ 'Text "cd", 'Text "fg", 'Text "h"]
data SplitOn :: Text -> Text -> Exp [Text]
type instance Eval (SplitOn ('Text sep) ('Text txt)) =
    Eval (F.FMap FromSymbolList =<< SOLoop (T.ToList sep) '( '[], T.ToList txt))


-- | Helper for SplitOn
--
-- >>> :kind! Eval (SOTake '["a", "b"] '[ "c", "d", "a", "b", "f", "g"] '[])
-- Eval (SOTake '["a", "b"] '[ "c", "d", "a", "b", "f", "g"] '[]) :: ([Symbol],
--                                                                    [Symbol])
-- = '( '["c", "d"], '["f", "g"])
data SOTake :: [Symbol] -> [Symbol] -> [Symbol] -> Exp ([Symbol], [Symbol])
type instance Eval (SOTake sep '[] accum) = '(accum, '[])
type instance Eval (SOTake sep (t ': txt) accum) = Eval
    (If (Eval (F.IsPrefixOf sep (t ': txt)))
        (Pure '(accum, Eval (F.Drop (Eval (F.Length sep)) (t ': txt))))
        (SOTake sep txt (Eval (accum ++ '[t])))
    )

-- | Helper for SplitOn
data SOLoop :: [Symbol] -> ([[Symbol]],[Symbol]) -> Exp [[Symbol]]
type instance Eval (SOLoop sep '(acc, '[])) = acc
type instance Eval (SOLoop sep '(acc, t ': txt)) =
    Eval (SOLoop sep =<< First (F.Snoc acc) =<< SOTake sep (t ': txt) '[])



-- | Split for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Split S.IsSpace (Eval (Singleton "cd bf abh")))
-- Eval (Split S.IsSpace (Eval (Singleton "cd bf abh"))) :: [Text]
-- = '[ 'Text "cd", 'Text "bf", 'Text "abh"]
data Split :: (Symbol -> Exp Bool) -> Text -> Exp [Text]
type instance Eval (Split p ('Text txt)) =
    Eval (F.FMap FromSymbolList =<< SplitLoop p '( '[], T.ToList txt))

-- | Helper for Split
data SplitTake :: (Symbol -> Exp Bool) -> [Symbol] -> [Symbol] -> Exp ([Symbol], [Symbol])
type instance Eval (SplitTake p '[] accum) = '(accum, '[])
type instance Eval (SplitTake p (t ': txt) accum) = Eval
    (If (Eval (p t))
        (Pure '(accum, txt))
        (SplitTake p txt (Eval (accum ++ '[t])))
    )

-- | Helper for Split
data SplitLoop :: (Symbol -> Exp Bool) -> ([[Symbol]],[Symbol]) -> Exp [[Symbol]]
type instance Eval (SplitLoop p '(acc, '[])) = acc
type instance Eval (SplitLoop p '(acc, t ': txt)) = 
    Eval (SplitLoop p =<< First (F.Snoc acc) =<< SplitTake p (t ': txt) '[])



-- | Lines for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Lines =<< Singleton "ok\nhmm\nab")
-- Eval (Lines =<< Singleton "ok\nhmm\nab") :: [Text]
-- = '[ 'Text "ok", 'Text "hmm", 'Text "ab"]
data Lines :: Text -> Exp [Text]
type instance Eval (Lines txt) = Eval (Split S.IsNewLine txt)

-- | Words for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (Words =<< Singleton "ok hmm\nab")
-- Eval (Words =<< Singleton "ok hmm\nab") :: [Text]
-- = '[ 'Text "ok", 'Text "hmm", 'Text "ab"]
data Words :: Text -> Exp [Text]
type instance Eval (Words txt) = Eval (Split S.IsSpaceDelim txt)

-- | Unlines for type-level text. This adds a newline to each Text and then
-- concats them.
--
-- === __Example__
--
-- >>> :kind! Eval (Unlines '[ 'Text "ok", 'Text "hmm", 'Text "ab"])
-- Eval (Unlines '[ 'Text "ok", 'Text "hmm", 'Text "ab"]) :: Text
-- = 'Text "ok\nhmm\nab\n"
data Unlines :: [Text] -> Exp Text
type instance Eval (Unlines txts) = 
    Eval (Concat =<< F.FMap (Flip Append (Singleton @@ "\n")) txts)

-- | Unwords for type-level text. This uses 'Intercalate' to add space-symbol
-- between the given texts.
--
-- === __Example__
--
-- >>> :kind! Eval (Unwords '[ 'Text "ok", 'Text "hmm", 'Text "ab"])
-- Eval (Unwords '[ 'Text "ok", 'Text "hmm", 'Text "ab"]) :: Text
-- = 'Text "ok hmm ab"
data Unwords :: [Text] -> Exp Text
type instance Eval (Unwords txts) = Eval (Intercalate ('Text " ") txts)


-- | IsPrefixOf for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (IsPrefixOf ('Text "aa") ('Text "aamiainen"))
-- Eval (IsPrefixOf ('Text "aa") ('Text "aamiainen")) :: Bool
-- = 'True
data IsPrefixOf :: Text -> Text -> Exp Bool
type instance Eval (IsPrefixOf ('Text l1) ('Text l2) ) =
    Eval (F.IsPrefixOf (T.ToList l1) (T.ToList l2))


-- | IsSuffixOf for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (IsSuffixOf ('Text "nen") ('Text "aamiainen"))
-- Eval (IsSuffixOf ('Text "nen") ('Text "aamiainen")) :: Bool
-- = 'True
data IsSuffixOf :: Text -> Text -> Exp Bool
type instance Eval (IsSuffixOf ('Text l1) ('Text l2) ) =
    Eval (F.IsSuffixOf (T.ToList l1) (T.ToList l2))


-- | IsInfixOf for type-level text.
--
-- === __Example__
--
-- >>> :kind! Eval (IsInfixOf ('Text "mia") ('Text "aamiainen"))
-- Eval (IsInfixOf ('Text "mia") ('Text "aamiainen")) :: Bool
-- = 'True
data IsInfixOf :: Text -> Text -> Exp Bool
type instance Eval (IsInfixOf ('Text l1) ('Text l2) ) =
    Eval (F.IsInfixOf (T.ToList l1) (T.ToList l2))