{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PackageImports    #-}
{-# LANGUAGE QuasiQuotes       #-}

import Criterion      ( bench, bgroup, env, nf )
import Criterion.Main ( defaultMain )

import qualified Data.ByteString as B
import qualified Data.Text       as T

import qualified "string-interpolate" Data.String.Interpolate             as SI
import qualified "interpolate" Data.String.Interpolate.IsString           as I
-- import           "Interpolation" Data.String.Interpolation                as N
import           "formatting" Formatting                                  ( (%) )
import qualified "formatting" Formatting                                  as F
import qualified "formatting" Formatting.ShortFormatters                  as F
-- import           "interpolatedstring-perl6" Text.InterpolatedString.Perl6 as P
import qualified "neat-interpolation" NeatInterpolation                   as NI

import Test.QuickCheck

--------------------------------------------------------------------------------
-- Interpolating Strings
--------------------------------------------------------------------------------

stringSI :: String -> String
stringSI str = [SI.i|A fine day to die, #{str}.|]

stringI :: String -> String
stringI str = [I.i|A fine day to die, #{str}.|]

stringF :: String -> String
stringF = F.formatToString ("A fine day to die, " % F.s % ".")

-- stringN :: String -> String
-- stringN s = [str|A fine day to die, $s$.|]

-- stringP :: String -> String
-- stringP str = [qc|A fine day to die, {str}.|]

--------------------------------------------------------------------------------
-- Interpolating Text
--------------------------------------------------------------------------------

textSI :: T.Text -> T.Text
textSI t = [SI.i|A fine day to die, #{t}.|]

textI :: T.Text -> T.Text
textI t = [I.i|A fine day to die, #{t}.|]

textF :: T.Text -> T.Text
textF = F.sformat ("A fine day to die, " % F.st % ".")

-- textN :: T.Text -> T.Text
-- textN t = [str|A fine day to die, $t$.|]

-- textP :: T.Text -> T.Text
-- textP t = [qc|A fine day to die, {t}.|]

textNI :: T.Text -> T.Text
textNI t = [NI.text|A fine day to die, $t.|]

--------------------------------------------------------------------------------
-- Interpolating ByteString
--------------------------------------------------------------------------------

byteStringSI :: B.ByteString -> B.ByteString
byteStringSI b = [SI.i|A fine day to die, #{b}.|]

byteStringI :: B.ByteString -> B.ByteString
byteStringI b = [I.i|A fine day to die, #{b}.|]

-- byteStringN :: B.ByteString -> B.ByteString
-- byteStringN b = [str|A fine day to die, $b$.|]

-- byteStringP :: B.ByteString -> B.ByteString
-- byteStringP b = [qc|A fine day to die, {b}.|]

--------------------------------------------------------------------------------
-- Multiple String interpolations
--------------------------------------------------------------------------------

multiStringSI :: (Int, String, Bool) -> String
multiStringSI (x, y, z) = [SI.i| foo #{x} bar #{y} baz #{z} quux |]

multiStringI :: (Int, String, Bool) -> String
multiStringI (x, y, z) = [I.i| foo #{x} bar #{y} baz #{z} quux |]

multiStringF :: (Int, String, Bool) -> String
multiStringF (x, y, z) =
  F.formatToString (" foo " % F.d % " bar " % F.s % " baz " % F.sh % " quux ") x y z

-- multiStringN :: (Int, String, Bool) -> String
-- multiStringN (x, y, z) = [str| foo $:x$ bar $y$ baz $:z$ quux |]

-- multiStringP :: (Int, String, Bool) -> String
-- multiStringP (x, y, z) = [qc| foo {x} bar {y} baz {z} quux |]

--------------------------------------------------------------------------------
-- Multiple Text interpolations
--------------------------------------------------------------------------------

multiTextSI :: (Int, T.Text, Bool) -> T.Text
multiTextSI (x, y, z) = [SI.i| foo #{x} bar #{y} baz #{z} quux |]

multiTextI :: (Int, T.Text, Bool) -> T.Text
multiTextI (x, y, z) = [I.i| foo #{x} bar #{y} baz #{z} quux |]

multiTextF :: (Int, T.Text, Bool) -> T.Text
multiTextF (x, y, z) =
  F.sformat (" foo " % F.d % " bar " % F.st % " baz " % F.sh % " quux ") x y z

-- multiTextN :: (Int, T.Text, Bool) -> T.Text
-- multiTextN (x, y, z) = [str| foo $:x$ bar $y$ baz $:z$ quux |]

-- multiTextP :: (Int, T.Text, Bool) -> T.Text
-- multiTextP (x, y, z) = [qc| foo {x} bar {y} baz {z} quux |]

multiTextNI :: (Int, T.Text, Bool) -> T.Text
multiTextNI (x, y, z) =
  let x' = T.pack $ show x
      z' = T.pack $ show z
  in [NI.text| foo $x' bar $y baz $z' quux |]

--------------------------------------------------------------------------------
-- Multiple ByteString interpolations
--------------------------------------------------------------------------------

multiByteStringSI :: (Int, B.ByteString, Bool) -> B.ByteString
multiByteStringSI (x, y, z) = [SI.i| foo #{x} bar #{y} baz #{z} quux |]

multiByteStringI :: (Int, B.ByteString, Bool) -> B.ByteString
multiByteStringI (x, y, z) = [I.i| foo #{x} bar #{y} baz #{z} quux |]

-- multiByteStringN :: (Int, B.ByteString, Bool) -> B.ByteString
-- multiByteStringN (x, y, z) = [str| foo $:x$ bar $y$ baz $:z$ quux |]

-- multiByteStringP :: (Int, B.ByteString, Bool) -> B.ByteString
-- multiByteStringP (x, y, z) = [qc| foo {x} bar {y} baz {z} quux |]

main :: IO ()
main = defaultMain $
  [ bgroup "Small Strings Bench" $
    [ bench "string-interpolate"       $ nf stringSI "William"
    , bench "interpolate"              $ nf stringI "William"
    , bench "formatting"               $ nf stringF "William"
    -- , bench "Interpolation"            $ nf stringN "William"
    -- , bench "interpolatedstring-perl6" $ nf stringP "William"
    ]
  , bgroup "Small Text Bench" $
    [ bench "string-interpolate"       $ nf textSI "William"
    , bench "interpolate"              $ nf textI "William"
    , bench "formatting"               $ nf textF "William"
    -- , bench "Interpolation"            $ nf textN "William"
    -- , bench "interpolatedstring-perl6" $ nf textP "William"
    , bench "neat-interpolation"       $ nf textNI "William"
    ]
  , bgroup "Small ByteString Bench" $
    [ bench "string-interpolate"       $ nf byteStringSI "William"
    , bench "interpolate"              $ nf byteStringI "William"
    -- "formatting" doesn't support ByteStrings.
    -- , bench "Interpolation"            $ nf byteStringN "William"
    -- , bench "interpolatedstring-perl6" $ nf byteStringP "William"
    ]
  , bgroup "Multiple Interpolations String Bench" $
    [ bench "string-interpolate"       $ nf multiStringSI (42, "CATALLAXY", True)
    , bench "interpolate"              $ nf multiStringI (42, "CATALLAXY", True)
    , bench "formatting"               $ nf multiStringF (42, "CATALLAXY", True)
    -- , bench "Interpolation"            $ nf multiStringN (42, "CATALLAXY", True)
    -- , bench "interpolatedstring-perl6" $ nf multiStringP (42, "CATALLAXY", True)
    ]
  , bgroup "Multiple Interpolations Text Bench" $
    [ bench "string-interpolate"       $ nf multiTextSI (42, "CATALLAXY", True)
    , bench "interpolate"              $ nf multiTextI (42, "CATALLAXY", True)
    , bench "formatting"               $ nf multiTextF (42, "CATALLAXY", True)
    -- , bench "Interpolation"            $ nf multiTextN (42, "CATALLAXY", True)
    -- , bench "interpolatedstring-perl6" $ nf multiTextP (42, "CATALLAXY", True)
    , bench "neat-interpolation"       $ nf multiTextNI (42, "CATALLAXY", True)
    ]
  , bgroup "Multiple Interpolations ByteString Bench" $
    [ bench "string-interpolate"       $ nf multiByteStringSI (42, "CATALLAXY", True)
    , bench "interpolate"              $ nf multiByteStringI (42, "CATALLAXY", True)
    -- "formatting" doesn't support ByteStrings.
    -- , bench "Interpolation"            $ nf multiByteStringN (42, "CATALLAXY", True)
    -- , bench "interpolatedstring-perl6" $ nf multiByteStringP (42, "CATALLAXY", True)
    ]
  , env largeishText $ \ ~t -> bgroup "Largeish Text Bench" $
    [ bench "string-interpolate"       $ nf textSI t
    , bench "interpolate"              $ nf textI t
    , bench "formatting"               $ nf textF t
    -- , bench "Interpolation"            $ nf textN t
    -- , bench "interpolatedstring-perl6" $ nf textP t
    , bench "neat-interpolation"       $ nf textNI t
    ]
  , env largeishByteString $ \ ~bs -> bgroup "Largeish ByteString Bench" $
    [ bench "string-interpolate"       $ nf byteStringSI bs
    , bench "interpolate"              $ nf byteStringI bs
    -- "formatting" doesn't support ByteStrings.
    -- , bench "Interpolation"            $ nf byteStringN bs
    -- , bench "interpolatedstring-perl6" $ nf byteStringP bs
    ]
  ]

largeishText :: IO T.Text
largeishText =
  generate $ T.pack <$> Prelude.take 100000 <$> infiniteListOf arbitrary

largeishByteString :: IO B.ByteString
largeishByteString =
  generate $ B.pack <$> Prelude.take 100000 <$> infiniteListOf arbitrary
