-- | Work in progress.
{-# LANGUAGE CPP
            ,MultiParamTypeClasses
            ,TypeFamilies
            ,FlexibleInstances #-}

module Data.ListLike.Chars

where

#if !MIN_VERSION_base(4,8,0)
import           Control.Applicative
import           Data.Monoid
#endif
import           Control.DeepSeq
--import           Control.Monad
import           Data.String as String (IsString(fromString))
import           Data.Semigroup (Semigroup(..))
import qualified Data.Text.Lazy as T
--import qualified Data.Text.Lazy.IO as TI
import qualified Data.Text.Lazy.Builder as Builder
--import           Data.Text.Encoding (decodeUtf8)
import           Data.ListLike.Base as LL
import           Data.ListLike.FoldableLL as LL
import           Data.ListLike.IO
import           Data.ListLike.String as LL
import           Data.ListLike.Text ()
import           GHC.Exts (IsList(..))

data Chars
    = B Builder.Builder
    | T T.Text
    deriving (Show, Eq, Ord)

builder :: Chars -> Builder.Builder
builder (B x) = x
builder (T s) = Builder.fromLazyText s
{-# INLINE builder #-}

instance Semigroup Chars where
  (<>) = mappend

instance Monoid Chars where
    mempty = B mempty
    mappend a b = B $ mappend (builder a) (builder b)

instance String.IsString Chars where
  -- Builder already has an IsString instance, do we want to use it?
  -- fromString = B . String.fromString
  -- or do we want the implementation that used to be in the StringLike instance?
  fromString = B . Builder.fromLazyText . LL.fromString

instance FoldableLL Chars Char where
    foldl f r0 (B b) = LL.foldl f r0 . Builder.toLazyText $ b
    foldl f r0 (T s) = LL.foldl f r0 $ s
    foldr f r0 (B b) = LL.foldr f r0 . Builder.toLazyText $ b
    foldr f r0 (T s) = LL.foldr f r0 $ s
    --
    foldl' f r0 (B b) = LL.foldl' f r0 . Builder.toLazyText $ b
    foldl' f r0 (T s) = LL.foldl' f r0 $ s
    foldl1 f (B b) = LL.foldl1 f . Builder.toLazyText $ b
    foldl1 f (T s) = LL.foldl1 f $ s
    foldr' f r0 (B b) = LL.foldr' f r0 . Builder.toLazyText $ b
    foldr' f r0 (T s) = LL.foldr' f r0 $ s
    foldr1 f (B b) = LL.foldr1 f . Builder.toLazyText $ b
    foldr1 f (T s) = LL.foldr1 f $ s

instance IsList Chars where
    type Item Chars = Char
    toList = LL.toList'
    fromList = LL.fromList'

instance ListLike Chars Char where
    singleton = B . Builder.singleton
    uncons (B b) =
        case LL.uncons (Builder.toLazyText b) of
          Nothing -> Nothing
          Just (c, s) -> Just (c, T s)
    uncons (T s) =
        case LL.uncons s of
          Nothing -> Nothing
          Just (c, s') -> Just (c, T s')
    null (B b) = LL.null . Builder.toLazyText $ b
    null (T t) = LL.null t

instance ListLikeIO Chars Char where
    hGetLine h = T <$> hGetLine h
    hGetContents h = T <$> hGetContents h
    hGet h n = T <$> hGet h n
    hGetNonBlocking h n = T <$> hGetNonBlocking h n
    hPutStr h (B b) = hPutStr h . Builder.toLazyText $ b
    hPutStr h (T s) = hPutStr h $ s

instance StringLike Chars where
    toString (B b) = toString . Builder.toLazyText $ b
    toString (T s) = toString $ s
    fromLazyText = B . Builder.fromLazyText
    fromText = B . Builder.fromText

instance NFData Chars where
    rnf (B b) = rnf . Builder.toLazyText $ b
    rnf (T s) = rnf s