{-# LANGUAGE GADTs #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}


module Fmt.Internal.Core where

#if __GLASGOW_HASKELL__ < 804
import           Data.Monoid ((<>))
#endif
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.IO as TL
import           Data.Text.Lazy.Builder hiding (fromString)
import           Formatting.Buildable (Buildable(..))


----------------------------------------------------------------------------
-- Class
----------------------------------------------------------------------------

class FromBuilder a where
  -- | Convert a 'Builder' to something else.
  fromBuilder :: Builder -> a

instance FromBuilder Builder where
  fromBuilder = id
  {-# INLINE fromBuilder #-}

instance (a ~ Char) => FromBuilder [a] where
  fromBuilder = TL.unpack . toLazyText
  {-# INLINE fromBuilder #-}

instance FromBuilder T.Text where
  fromBuilder = TL.toStrict . toLazyText
  {-# INLINE fromBuilder #-}

instance FromBuilder TL.Text where
  fromBuilder = toLazyText
  {-# INLINE fromBuilder #-}

instance (a ~ ()) => FromBuilder (IO a) where
  fromBuilder = TL.putStr . toLazyText
  {-# INLINE fromBuilder #-}

----------------------------------------------------------------------------
-- Operators
----------------------------------------------------------------------------

-- | Concatenate, then convert.
(+|) :: (FromBuilder b) => Builder -> Builder -> b
(+|) str rest = fromBuilder (str <> rest)

-- | 'build' and concatenate, then convert.
(|+) :: (Buildable a, FromBuilder b) => a -> Builder -> b
(|+) a rest = fromBuilder (build a <> rest)

infixr 1 +|
infixr 1 |+

-- | Concatenate, then convert.
(+||) :: (FromBuilder b) => Builder -> Builder -> b
(+||) str rest = str +| rest
{-# INLINE (+||) #-}

-- | 'show' and concatenate, then convert.
(||+) :: (Show a, FromBuilder b) => a -> Builder -> b
(||+) a rest = show a |+ rest
{-# INLINE (||+) #-}

infixr 1 +||
infixr 1 ||+

(|++|) :: (Buildable a, FromBuilder b) => a -> Builder -> b
(|++|) a rest = fromBuilder (build a <> rest)
{-# INLINE (|++|) #-}

(||++||) :: (Show a, FromBuilder b) => a -> Builder -> b
(||++||) a rest = show a |+ rest
{-# INLINE (||++||) #-}

(||++|) :: (Buildable a, FromBuilder b) => a -> Builder -> b
(||++|) a rest = a |++| rest
{-# INLINE (||++|) #-}

(|++||) :: (Show a, FromBuilder b) => a -> Builder -> b
(|++||) a rest = a ||++|| rest
{-# INLINE (|++||) #-}

infixr 1 |++|
infixr 1 ||++||
infixr 1 ||++|
infixr 1 |++||

----------------------------------------------------------------------------
-- Functions
----------------------------------------------------------------------------

{- | 'fmt' converts things to 'String', 'Text' or 'Builder'.

Most of the time you won't need it, as strings produced with ('+|') and
('|+') can already be used as 'String', 'Text', etc. However, combinators
like 'listF' can only produce 'Builder' (for better type inference), and you
need to use 'fmt' on them.

Also, 'fmt' can do printing:

>>> fmt "Hello world!\n"
Hello world!
-}
fmt :: FromBuilder b => Builder -> b
fmt = fromBuilder
{-# INLINE fmt #-}

{- | Like 'fmt', but appends a newline.
-}
fmtLn :: FromBuilder b => Builder -> b
fmtLn = fromBuilder . (<> "\n")
{-# INLINE fmtLn #-}