content: {-# LANGUAGE OverloadedStrings #-}
  {-# OPTIONS -Wno-type-defaults #-}
  
  import Control.Monad
  import Data.Char (isSpace, isUpper)
  import Data.Int
  import qualified Data.Monoid
  import Data.Scientific
  import qualified Data.Semigroup
  import qualified Data.Text.Lazy as LT
  import Data.Time (Day)
  import Formatting as F
  import Formatting.Time
  import Test.Hspec
  
  main :: IO ()
  main = hspec spec
  
  spec :: Spec
  spec = do
    describe "Regression tests" $ do
      describe "https://github.com/AJChapman/formatting/issues/36" $ do
        it "format (later id <> later id) \"x\"" $ format (later id Data.Monoid.<> later id)    "x" `shouldBe` "xx"
        it "format (later id <> later id) \"x\"" $ format (later id Data.Semigroup.<> later id) "x" `shouldBe` "xx"
  
      describe "https://github.com/AJChapman/formatting/issues/31" $
        it "10^16-1" $ F.format F.int (10 ^ (16 :: Int) - 1 :: Int) `shouldBe` "9999999999999999"
  
      describe "https://github.com/AJChapman/formatting/issues/28" $ do
        it "-100"         $ sformat (groupInt 3 ',') (-100 :: Int)       `shouldBe` "-100"
        it "-100,000,000" $ sformat (groupInt 3 ',') (-100000000 :: Int) `shouldBe` "-100,000,000"
        it "100,000,000"  $ sformat (groupInt 3 ',') (100000000 :: Int)  `shouldBe` "100,000,000"
  
      describe "https://github.com/bos/text-format/issues/18" $ do
        it "build (minBound :: Int)" $ format build (minBound :: Int64) `shouldBe` "-9223372036854775808"
        it "build (maxBound :: Int)" $ format build (maxBound :: Int)   `shouldBe` "9223372036854775807"
  
      describe "https://github.com/AJChapman/formatting/issues/62" $ do
        it "left 3 '0' (0 :: Int)"  $ format (left 3 '0') (0 ::Int)  `shouldBe` "000"
        it "left 3 '0' (0 :: Word)" $ format (left 3 '0') (0 ::Word) `shouldBe` "000"
  
      describe "https://github.com/AJChapman/formatting/issues/60" $
        it "build (minBound :: Word)" $ format build (minBound :: Word) `shouldBe` "0"
  
      describe "https://github.com/AJChapman/formatting/issues/59" $
        it "shortest not scientific" $ format shortest (0.01 :: Double) `shouldBe` "0.01"
  
    describe "Floating point" $ do
      it "Fixed"    $ format (fixed 4) (12.123456 :: Double) `shouldBe` "12.1235"
      it "Variable" $ format float     (12.123456 :: Double) `shouldBe` "12.123456"
      it "Shortest" $ format shortest  (12.0000 :: Double) `shouldBe` "12"
  
    describe "Scientific" $ do
      it "sci" $ format sci (scientific 60221409 16) `shouldBe` "6.0221409e23"
      it "scifmt" $ format (scifmt Exponent (Just 3)) (scientific 60221409 16) `shouldBe` "6.022e23"
      it "scifmt" $ format (scifmt Exponent Nothing) (scientific 60221409 16) `shouldBe` "6.0221409e23"
      it "scifmt" $ format (scifmt Fixed Nothing) (scientific 60221409 16) `shouldBe` "602214090000000000000000.0"
      it "scifmt" $ format (scifmt Generic (Just 5)) (scientific 60221409 16) `shouldBe` "6.02214e23"
  
    describe "Bytes" $ do
      it "1KB" $ format (bytes shortest) (1024 :: Int) `shouldBe` "1KB"
      it "1.15GB" $ format (bytes (fixed 2)) (1234567890 :: Int) `shouldBe` "1.15GB"
  
    describe
      "Buildable a => Buildable [a]"
      (do it "\"\" :: [Char] (backwards compatibility)"
             (shouldBe (format build ("" :: String)) "")
          it "\"hi\" :: [Char] (backwards compatibility)"
             (shouldBe (format build ("hi" :: String)) "hi")
          it "[1,2,3] :: [Int]"
             (shouldBe (format build ([1,2,3] :: [Int])) "[1,2,3]")
          it "[] :: [Int]"
             (shouldBe (format build ([] :: [Int])) "[]"))
  
    describe "ords" $ do
        let tests :: [(Int, String)]
            tests = [ ( 1, "1st")
                    , ( 2, "2nd")
                    , ( 3, "3rd")
                    , ( 4, "4th")
                    , ( 5, "5th")
                    , ( 6, "6th")
                    , ( 7, "7th")
                    , ( 8, "8th")
                    , ( 9, "9th")
                    , (10, "10th")
                    , (11, "11th")
                    , (12, "12th")
                    , (13, "13th")
                    , (14, "14th")
                    , (15, "15th")
                    , (16, "16th")
                    , (17, "17th")
                    , (18, "18th")
                    , (19, "19th")
                    , (20, "20th")
                    , (21, "21st")
                    , (22, "22nd")
                    , (23, "23rd")
                    , (24, "24th")
                    , (25, "25th")
                    , (26, "26th")
                    , (27, "27th")
                    , (28, "28th")
                    , (29, "29th")
                    , (30, "30th")
                    , (31, "31st")
                    , (31, "31st")
                    , (32, "32nd")
                    , (33, "33rd")
                    , (34, "34th")
                    ]
  
        forM_ tests $ \(input, output) -> it output $ format ords input `shouldBe` LT.pack output
  
    describe "plural" $ do
      let formatPeople = format (int % " " <> plural "person" "people" % ".")
      it "formats a person" $ formatPeople (1 :: Int) `shouldBe` "1 person."
      it "formats a person" $ formatPeople (3 :: Int) `shouldBe` "3 people."
  
    describe "diffComponents" $ do
      it "59s" $ flip shouldBe "00:00:00:59" $ format diffComponents (59 :: Double)
      it "minute" $ flip shouldBe "00:00:01:00" $ format diffComponents (60 :: Double)
      it "90s" $ flip shouldBe "00:00:01:30" $ format diffComponents (90 :: Double)
      it "hour" $ flip shouldBe "00:01:00:00" $ format diffComponents (3600 :: Double)
      it "day" $ flip shouldBe "01:00:00:00" $ format diffComponents (86400 :: Double)
  
    describe "container formatters" $ do
      it "maybed Nothing" $ format (maybed "Goodbye" text) Nothing `shouldBe` "Goodbye"
      it "maybed Just" $ format (maybed "Goodbye" text) (Just "Hello") `shouldBe` "Hello"
      it "optioned Nothing" $ format (optioned text) Nothing `shouldBe` ""
      it "optioned Just" $ format (optioned text) (Just "Hello") `shouldBe` "Hello"
      it "eithered Left" $ format (eithered text int) (Left "Error!") `shouldBe` "Error!"
      it "eithered Right" $ format (eithered text int) (Right 69) `shouldBe` "69"
      it "lefted Left" $ format (lefted text) (Left "bingo") `shouldBe` "bingo"
      it "lefted Right" $ format (lefted text) (Right 16) `shouldBe` ""
      it "righted Left" $ format (righted text) (Left 16) `shouldBe` ""
      it "righted Right" $ format (righted text) (Right "bingo") `shouldBe` "bingo"
  
    describe "list formatters" $ do
      it "concatenated" $ format (concatenated text) ["one", "two", "three"] `shouldBe` "onetwothree"
      it "joinedWith" $ format (joinedWith (mconcat . reverse) int) [123, 456, 789] `shouldBe` "789456123"
      it "intercalated" $ format (intercalated "||" int) [1, 2, 3]  `shouldBe` "1||2||3"
      it "unworded" $ format (unworded int) [1, 2, 3] `shouldBe` "1 2 3"
      it "unlined" $ format (unlined char) ['a'..'c'] `shouldBe` "a\nb\nc\n"
      it "spaced" $ format (spaced int) [1, 2, 3] `shouldBe` "1 2 3"
      it "commaSep" $ format (took 5 (commaSep int)) [1..] `shouldBe` "1,2,3,4,5"
      it "commaSpaceSep" $ format (took 3 (commaSpaceSep ords)) [1..] `shouldBe` "1st, 2nd, 3rd"
      it "list" $ format (list stext) ["one", "two", "three"] `shouldBe` "[one, two, three]"
      it "qlist" $ format (qlist stext) ["one", "two", "three"] `shouldBe` "[\"one\", \"two\", \"three\"]"
      it "took" $ format (took 7 (list bin)) [1..] `shouldBe` "[1, 10, 11, 100, 101, 110, 111]"
      it "dropped" $ format (dropped 3 (list int)) [1..6] `shouldBe` "[4, 5, 6]"
  
    describe "splitting formatters" $ do
      it "splat" $ format (splat isSpace commaSpaceSep stext) "This\t  is\n\t\t  poorly formatted   " `shouldBe` "This, , , is, , , , , poorly, formatted, , , "
      it "splatWith" $ format (splatWith (LT.chunksOf 3) list int) 1234567890 `shouldBe` "[123, 456, 789, 0]"
      it "splatOn" $ format (splatOn "," unlined text) "one,two,three" `shouldBe` "one\ntwo\nthree\n"
      it "worded" $ format (worded list text) "one  two three  " `shouldBe` "[one, two, three]"
      it "lined" $ format (lined qlist text) "one two three\n\nfour five six\nseven eight nine\n\n" `shouldBe` "[\"one two three\", \"\", \"four five six\", \"seven eight nine\", \"\"]"
  
    describe "altering combinators" $ do
      it "alteredWith" $ format (alteredWith LT.reverse int) 123456 `shouldBe` "654321"
      it "charsKeptIf" $ format (charsKeptIf isUpper text) "Data.Char.isUpper" `shouldBe` "DCU"
      it "charsRemovedIf" $ format (charsRemovedIf isUpper text) "Data.Char.isUpper" `shouldBe` "ata.har.ispper"
      it "replaced" $ format (replaced "Bruce" "<redacted>" stext) "Bruce replied that Bruce's name was, in fact, '<redacted>'." `shouldBe` "<redacted> replied that <redacted>'s name was, in fact, '<redacted>'."
      it "uppercased" $ format (uppercased text) "I'm not shouting, you're shouting." `shouldBe` "I'M NOT SHOUTING, YOU'RE SHOUTING."
      it "lowercased" $ format (lowercased text) "Cd SrC/; Rm -Rf *" `shouldBe` "cd src/; rm -rf *"
      it "titlecased" $ format (titlecased string) "the life of brian" `shouldBe` "The Life Of Brian"
      it "ltruncated" $ format (ltruncated 5 text) "hellos" `shouldBe` "he..."
      it "ltruncated, non-truncated" $ format (ltruncated 5 text) "hello" `shouldBe` "hello"
      it "rtruncated" $ format (rtruncated 5 text) "hellos" `shouldBe` "...os"
      it "rtruncated, non-truncated" $ format (rtruncated 5 text) "hello" `shouldBe` "hello"
      it "ctruncated" $ format (ctruncated 15 4 text) "The quick brown fox jumps over the lazy dog." `shouldBe` "The quick brown...dog."
      it "ctruncated, non-truncated" $ format (ctruncated 15 4 text) "The quick brown fox" `shouldBe` "The quick brown fox"
      it "lpadded" $ format (lpadded 7 ' ' int) 1 `shouldBe` "      1"
      it "lpadded doesn't shorten" $ format (lpadded 7 ' ' int) 123456789 `shouldBe` "123456789"
      it "rpadded" $ format (rpadded 7 ' ' int) 1 `shouldBe` "1      "
      it "cpadded" $ format (cpadded 7 ' ' int) 1 `shouldBe` "   1   "
      it "lfixed short" $ format (lfixed 10 ' ' int) 123 `shouldBe` "123       "
      it "lfixed at length" $ format (lfixed 10 ' ' int) 1234567890 `shouldBe` "1234567890"
      it "lfixed long" $ format (lfixed 10 ' ' int) 123456789012345 `shouldBe` "1234567..."
      it "rfixed short" $ format (rfixed 10 ' ' int) 123 `shouldBe` "       123"
      it "rfixed at length" $ format (rfixed 10 ' ' int) 1234567890 `shouldBe` "1234567890"
      it "rfixed long" $ format (rfixed 10 ' ' int) 123456789012345 `shouldBe` "...9012345"
      it "cfixed short" $ format (cfixed 4 3 ' ' int) 123 `shouldBe` "    123   "
      it "cfixed at length" $ format (cfixed 4 3 ' ' int) 1234567890 `shouldBe` "1234567890"
      it "cfixed long" $ format (cfixed 4 3 ' ' int) 123456789012345 `shouldBe` "1234...345"
  
    describe "wrapping combinators" $ do
      it "prefixed" $ format ("The answer is: " % prefixed "wait for it... " int) 42 `shouldBe` "The answer is: wait for it... 42"
      it "prefixed, combining" $ format (unlined (indented 4 (prefixed "- " int))) [1, 2, 3] `shouldBe` "    - 1\n    - 2\n    - 3\n"
      it "suffixed" $ format (suffixed "!!!" int) 7 `shouldBe` "7!!!"
      it "surrounded" $ format (surrounded "***" string) "glue" `shouldBe` "***glue***"
      it "enclosed" $ format (enclosed "<!--" "-->" text) "an html comment" `shouldBe` "<!--an html comment-->"
      it "squoted" $ let obj :: Maybe (Maybe Int); obj = Just Nothing in format ("The object is: " % squoted shown % ".") obj  `shouldBe` "The object is: 'Just Nothing'."
      it "dquoted" $ format ("He said it was based on " % dquoted stext % ".") "science" `shouldBe` "He said it was based on \"science\"."
      it "parenthesised" $ format (took 5 (list (parenthesised int))) [1..] `shouldBe` "[(1), (2), (3), (4), (5)]"
      it "squared" $ format (squared int) 7 `shouldBe` "[7]"
      it "braced" $ format ("\\begin" % braced text) "section" `shouldBe` "\\begin{section}"
      it "angled" $ format (list (angled text)) ["html", "head", "title", "body", "div", "span"] `shouldBe` "[<html>, <head>, <title>, <body>, <div>, <span>]"
      it "backticked" $ format ("Be sure to run" %+ backticked builder %+ "as root.") ":(){:|:&};:" `shouldBe` "Be sure to run `:(){:|:&};:` as root."
  
    describe "indenters" $ do
      it "indented" $ format (indented 4 int) 7 `shouldBe` "    7"
      it "indentedLines" $ format ("The lucky numbers are:\n" % indentedLines 4 int) [7, 13, 1, 42] `shouldBe` "The lucky numbers are:\n    7\n    13\n    1\n    42\n"
      it "reindented" $ format (reindented 2 text) "one\ntwo\nthree" `shouldBe` "  one\n  two\n  three\n"
  
    describe "numerical adapters" $ do
      it "roundedTo" $ format (list (roundedTo int)) [10.66, 6.66, 1.0, 3.4] `shouldBe` "[11, 7, 1, 3]"
      it "truncatedTo" $ format (list (truncatedTo int)) [10.66, 6.66, 1.0, 3.4] `shouldBe` "[10, 6, 1, 3]"
      it "ceilingedTo" $ format (list (ceilingedTo int)) [10.66, 6.66, 1.0, 3.4] `shouldBe` "[11, 7, 1, 4]"
      it "flooredTo" $ format (list (flooredTo int)) [10.66, 6.66, 1.0, 3.4] `shouldBe` "[10, 6, 1, 3]"
  
    describe "structure formatting" $
      it "accessed" $ format (accessed fst int) (1, "hello") `shouldBe` "1"
    -- describe "lens formatters" $ do
    --   it "viewed" $ flip shouldBe "(viewed _1 int) (1, "hello")" $ format
  
    describe "fixed-width numbers" $ do
      it "binPrefix" $ format (binPrefix 16) 4097 `shouldBe` "0b0001000000000001"
      it "octPrefix" $ format (octPrefix 16) 4097 `shouldBe` "0o0000000000010001"
      it "hexPrefix" $ format (hexPrefix 16) 4097 `shouldBe` "0x0000000000001001"
  
    describe "mappend with added space (<%+>)" $ do
      let testTime = (read "2022-06-06") :: Day
      it "combines formatters, adding a space" $ format (year <%+> month <%+> dayOfMonth) testTime `shouldBe` "2022 06 06"
  