{-# LANGUAGE DeriveDataTypeable, RecordWildCards, TemplateHaskell, MagicHash #-}
{-# OPTIONS_GHC -fno-warn-missing-fields -fno-warn-unused-binds #-}

module System.Console.CmdArgs.Test.Implicit.Tests(test, demos) where

import System.Console.CmdArgs
import System.Console.CmdArgs.Explicit(modeHelp)
import System.Console.CmdArgs.Test.Implicit.Util
import System.Console.CmdArgs.Quote
import Data.Int
import Data.Ratio


-- from bug #256 and #231
data Test1
    = Test1 {maybeInt :: Maybe Int, listDouble :: [Double], maybeStr :: Maybe String, float :: Float
            ,bool :: Bool, maybeBool :: Maybe Bool, listBool :: [Bool], int64 :: Int64}
      deriving (Show,Eq,Data,Typeable)

def1 = Test1 def def def (def &= args) def def def def
mode1 = cmdArgsMode def1

$(cmdArgsQuote [d|
    mode1_ = cmdArgsMode# def1_
    def1_ = Test1 def def def (def &=# args) def def def def
    |])

test1 = do
    let Tester{..} = testers "Test1" [mode1,mode1_]
    [] === def1
    ["--maybeint=12"] === def1{maybeInt = Just 12}
    ["--maybeint=12","--maybeint=14"] === def1{maybeInt = Just 14}
    fails ["--maybeint"]
    fails ["--maybeint=test"]
    ["--listdouble=1","--listdouble=3","--listdouble=2"] === def1{listDouble=[1,3,2]}
    fails ["--maybestr"]
    ["--maybestr="] === def1{maybeStr=Just ""}
    ["--maybestr=test"] === def1{maybeStr=Just "test"}
    ["12.5"] === def1{float=12.5}
    ["12.5","18"] === def1{float=18}
    ["--bool"] === def1{bool=True}
    ["--maybebool"] === def1{maybeBool=Just True}
    ["--maybebool=off"] === def1{maybeBool=Just False}
    ["--listbool","--listbool=true","--listbool=false"] === def1{listBool=[True,True,False]}
    ["--int64=12"] === def1{int64=12}
    fails ["--listbool=fred"]
    invalid $ \_ -> def1{listBool = def &= opt "yes"}


-- from bug #230
data Test2 = Cmd1 {bs :: [String]}
           | Cmd2 {bar :: Int}
             deriving (Show, Eq, Data, Typeable)

mode2 = cmdArgsMode $ modes [Cmd1 [], Cmd2 42]

test2 = do
    let Tester{..} = tester "Test2" mode2
    fails []
    ["cmd1","-btest"] === Cmd1 ["test"]
    ["cmd2","-b14"] === Cmd2 14


-- various argument position
data Test3 = Test3 {pos1_1 :: [Int], pos1_2 :: [String], pos1_rest :: [String]}
             deriving (Show, Eq, Data, Typeable)

mode3 = cmdArgsMode $ Test3 (def &= argPos 1) (def &= argPos 2 &= opt "foo") (def &= args)

$(cmdArgsQuote [d| mode3_ = cmdArgsMode# $ Test3 (def &=# argPos 1) (def &=# argPos 2 &=# opt "foo") (def &=# args) |])


test3 = do
    let Tester{..} = testers "Test3" [mode3,mode3_]
    fails []
    fails ["a"]
    ["a","1"] === Test3 [1] ["foo"] ["a"]
    ["a","1","c"] === Test3 [1] ["c"] ["a"]
    ["a","1","c","d"] === Test3 [1] ["c"] ["a","d"]
    invalid $ \_ -> Test3 def def (def &= help "help" &= args)


-- from bug #222
data Test4 = Test4 {test_4 :: [String]}
             deriving (Show, Eq, Data, Typeable)

mode4 = cmdArgsMode $ Test4 (def &= opt "hello" &= args)

test4 = do
    let Tester{..} = tester "Test4" mode4
    [] === Test4 ["hello"]
    ["a"] === Test4 ["a"]
    ["a","b"] === Test4 ["a","b"]


-- from #292, automatic enumerations
data ABC = Abacus | Arbitrary | B | C deriving (Eq,Show,Data,Typeable)
data Test5 = Test5 {choice :: ABC} deriving (Eq,Show,Data,Typeable)

mode5 = cmdArgsMode $ Test5 B

test5 = do
    let Tester{..} = tester "Test5" mode5
    [] === Test5 B
    fails ["--choice=A"]
    ["--choice=c"] === Test5 C
    ["--choice=C"] === Test5 C
    ["--choice=Aba"] === Test5 Abacus
    ["--choice=abacus"] === Test5 Abacus
    ["--choice=c","--choice=B"] === Test5 B

-- tuple support
data Test6 = Test6 {val1 :: (Int,Bool), val2 :: [(Int,(String,Double))]} deriving (Eq,Show,Data,Typeable)
val6 = Test6 def def

mode6 = cmdArgsMode val6

test6 = do
    let Tester{..} = tester "Test6" mode6
    [] === val6
    ["--val1=1,True"] === val6{val1=(1,True)}
    ["--val1=84,off"] === val6{val1=(84,False)}
    fails ["--val1=84"]
    fails ["--val1=84,off,1"]
    ["--val2=1,2,3","--val2=5,6,7"] === val6{val2=[(1,("2",3)),(5,("6",7))]}

-- from #333, add default fields
data Test7 = Test71 {shared :: Int}
           | Test72 {unique :: Int, shared :: Int}
           | Test73 {unique :: Int, shared :: Int}
             deriving (Eq,Show,Data,Typeable)

mode7 = cmdArgsMode $ modes [Test71{shared = def &= name "rename"}, Test72{unique=def}, Test73{}]

test7 = do
    let Tester{..} = tester "Test7" mode7
    fails []
    ["test71","--rename=2"] === Test71 2
    ["test72","--rename=2"] === Test72 0 2
    ["test72","--unique=2"] === Test72 2 0
    ["test73","--rename=2"] === Test73 0 2
    ["test73","--unique=2"] === Test73 2 0

-- from #252, grouping
data Test8 = Test8 {test8a :: Int, test8b :: Int, test8c :: Int}
           | Test81
           | Test82
             deriving (Eq,Show,Data,Typeable)

mode8 = cmdArgsMode $ modes [Test8 1 (2 &= groupname "More flags") 3 &= groupname "Mode1", Test81, Test82 &= groupname "Mode2"]
mode8_ = cmdArgsMode_ $ modes_ [record Test8{} [atom (1::Int), atom (2::Int) += groupname "More flags", atom (3::Int)] += groupname "Mode1"
                               ,record Test81{} []
                               ,record Test82{} [] += groupname "Mode2"]

test8 = do
    let Tester{..} = testers "Test8" [mode8,mode8_]
    isHelp ["-?"] ["Flags:","  --test8a=INT","More flags:","  --test8b=INT"]
    fails []
    ["test8","--test8a=18"] === Test8 18 2 3

-- bug from Sebastian Fischer, enums with multiple fields
data XYZ = X | Y | Z deriving (Eq,Show,Data,Typeable)
data Test9 = Test91 {foo :: XYZ}
           | Test92 {foo :: XYZ}
             deriving (Eq,Show,Data,Typeable)

mode9 = cmdArgsMode $ modes [Test91 {foo = enum [X &= help "pick X (default)", Y &= help "pick Y"]} &= auto, Test92{}]
mode9_ = cmdArgsMode_ $ modes_ [record Test91{} [enum_ foo [atom X += help "pick X (default)", atom Y += help "pick Y"]] += auto, record Test92{} []]

test9 = do
    let Tester{..} = testers "Test9" [mode9,mode9_]
    [] === Test91 X
    ["test91","-x"] === Test91 X
    ["test91","-y"] === Test91 Y
    fails ["test91","-z"]
    ["test92","-x"] === Test92 X
    ["test92","-y"] === Test92 Y
    ["test92"] === Test92 X
    invalid $ \_ -> modes [Test91 {foo = enum [X &= help "pick X (default)"] &= opt "X"}]

-- share common fields in the help message
data Test10 = Test101 {food :: Int}
            | Test102 {food :: Int, bard :: Int}
              deriving (Eq,Show,Data,Typeable)

mode10 = cmdArgsMode $ modes [Test101 def, Test102 def def]

test10 = do
    let Tester{..} = tester "Test10" mode10
    isHelp ["-?=one"] ["  -f --food=INT"]
    isHelpNot ["-?=one"] ["  -b --bard=INT"]

-- test for GHC over-optimising
data Test11 = Test11A {test111 :: String}
            | Test11B {test111 :: String}
              deriving (Eq,Show,Data,Typeable)

test11A = Test11A { test111 = def &= argPos 0 }
test11B = Test11B { test111 = def &= argPos 0 }
mode11 = cmdArgsMode $ modes [test11A, test11B]

mode11_ = cmdArgsMode_ $ modes_
    [record Test11A{} [test111 := def += argPos 0]
    ,record Test11B{} [test111 := def += argPos 0]]

test11 = do
    let Tester{..} = testers "Test11" [mode11,mode11_]
    fails []
    ["test11a","test"] === Test11A "test"
    ["test11b","test"] === Test11B "test"


-- #351, check you can add name annotations to modes
data Test12 = Test12A | Test12B deriving (Eq,Show,Data,Typeable)

mode12 = cmdArgsMode $ modes [Test12A &= name "check", Test12B]
mode12_ = cmdArgsMode $ modes [Test12A &= name "check" &= explicit, Test12B]

test12 = do
    let Tester{..} = tester "Test12" mode12
    fails []
    ["test12a"] === Test12A
    ["check"] === Test12A
    ["test12b"] === Test12B
    fails ["t"]
    let Tester{..} = tester "Test12" mode12_
    fails []
    fails ["test12a"]
    ["check"] === Test12A
    ["test12b"] === Test12B
    ["t"] === Test12B


-- the ignore annotation and versionArg [summary]
data Test13 = Test13A {foo13 :: Int, bar13 :: Either Int Int}
            | Test13B {foo13 :: Int}
            | Test13C {foo13 :: Int}
              deriving (Eq,Show,Data,Typeable)

mode13 = cmdArgsMode $ modes [Test13A 1 (Left 1 &= ignore), Test13B 1 &= ignore, Test13C{}]
                       &= versionArg [summary "Version text here"]
                       &= summary "Help text here"

test13 = do
    let Tester{..} = tester "Test13" mode13
    fails ["test13b"]
    fails ["test13a --bar13=1"]
    ["test13a","--foo13=13"] === Test13A 13 (Left 1)
    ["test13c","--foo13=13"] === Test13C 13
    isHelp ["--help"] ["Help text here"]
    isVersion ["--version"] "Version text here"
    fails ["--numeric-version"]

-- check a list becomes modes not an enum
data Test14 = Test14A | Test14B | Test14C deriving (Eq,Show,Data,Typeable)

mode14 = cmdArgsMode $ modes [Test14A, Test14B, Test14C]

test14 = do
    let Tester{..} = tester "Test14" mode14
    fails []
    ["test14a"] === Test14A
    fails ["--test14a"]

-- custom help flags
data Test15 = Test15 {test15a :: Bool} deriving (Eq,Show,Data,Typeable)

mode15 = cmdArgsMode $ Test15 (False &= name "help")
         &= helpArg [groupname "GROUP", name "h", name "nohelp", explicit, help "whatever"] &= versionArg [ignore]
         &= verbosityArgs [ignore] [explicit,name "silent"]

$(cmdArgsQuote [d|
    mode15_ = cmdArgsMode# $ Test15 (False &=# name "help")
              &=# helpArg [groupname "GROUP", name "h", name "nohelp", explicit, help "whatever"] &=# versionArg [ignore]
              &=# verbosityArgs [ignore] [explicit,name "silent"]
    |])

test15 = do
    let Tester{..} = testers "Test15" [mode15,mode15_]
    invalid $ \_ -> Test15 (False &= name "help")
    ["--help"] === Test15 True
    ["-t"] === Test15 True
    fails ["-?"]
    isHelp ["--nohelp"] ["  -h --nohelp  whatever"]
    isHelp ["-h"] []
    isHelp ["-h"] ["GROUP:"]
    fails ["--version"]
    fails ["--numeric-version"]
    fails ["--verbose"]
    fails ["--quiet"]
    isVerbosity ["--help","--silent"] Quiet

-- check newtype support
newtype MyInt = MyInt Int deriving (Eq,Show,Data,Typeable)

data Test16 = Test16 {test16a :: MyInt, test16b :: [MyInt]} deriving (Eq,Show,Data,Typeable)

mode16 = cmdArgsMode $ Test16 (MyInt 12) [] &= summary "The Glorious Glasgow Haskell Compilation System, version 7.6.3"

test16 = do
    let Tester{..} = tester "Test16" mode16
    [] === Test16 (MyInt 12) []
    isVersion ["--numeric-version"] "7.6.3"
    fails ["--test16a"]
    ["--test16a=5"] === Test16 (MyInt 5) []
    ["--test16b=5","--test16b=82"] === Test16 (MyInt 12) [MyInt 5, MyInt 82]

-- #552, @ directives not expanded after -- symbols
-- not actually checked because this path doesn't go through processArgs
data Test17 = Test17 {test17_ :: [String]} deriving (Eq,Show,Data,Typeable)

mode17 = cmdArgsMode $ Test17 ([] &= args) &= noAtExpand &= summary "bzip2 3.5-windows version"

test17 = do
    let Tester{..} = tester "Test17" mode17
    [] === Test17 []
    ["test","of","this"] === Test17 ["test","of","this"]
    ["test","--","@foo"] === Test17 ["test","@foo"]
    isVersion ["--numeric-version"] "3.5-windows"


data Debuggable = This | That deriving (Eq,Show,Data,Typeable)
data Test18 = Test18 {test18_ :: [Debuggable]} deriving (Eq,Show,Data,Typeable)

mode18 = cmdArgsMode $ Test18 $ enum [[] &= ignore, [This] &= name "debug-this", [That] &= name "debug-that"]

test18 = do
    let Tester{..} = tester "Test18" mode18
    [] === Test18 []
    ["--debug-this","--debug-that","--debug-this"] === Test18 [This,That,This]

-- #610, check performance for long lists (took ~20s before)

data Test19 = Test19 {test19_ :: [String]} deriving (Eq,Show,Data,Typeable)

mode19 = cmdArgsMode $ Test19 ([] &= args)

test19 = do
    let Tester{..} = tester "Test19" mode19
    let args = map show [1..1000]
    args === Test19 args


-- #615, newtype wrappers of lists/Maybe should accumulate properly

newtype Test20A = Test20A [String] deriving (Eq,Show,Data,Typeable)
data Test20 = Test20 {test20_ :: Test20A} deriving (Eq,Show,Data,Typeable)

mode20 = cmdArgsMode $ Test20 (Test20A [] &= args)

test20 = do
    let Tester{..} = tester "Test20" mode20
    ["a","b","c"] === Test20 (Test20A ["a","b","c"])


-- #626, don't reverse values too much

newtype Test21A = Test21A [String] deriving (Eq,Show,Data,Typeable)
data Test21 = Test21 {test21A :: Test21A, test21B :: [String], test21C :: [Int]} deriving (Eq,Show,Data,Typeable)

mode21 = cmdArgsMode $ Test21 (Test21A ["a","b","c"]) ["A","B","C"] [1,2,3]

test21 = do
    let Tester{..} = tester "Test21" mode21
    [] === Test21 (Test21A ["a","b","c"]) ["A","B","C"] [1,2,3]

-- #10, don't break elm-server

data Test22 = Test22 {port :: Int, runtime :: Maybe FilePath} deriving (Data,Typeable,Show,Eq)

mode22 = cmdArgsMode $ Test22
  { port = 8000 &= help "set the port of the server"
  , runtime = Nothing &= typFile
              &= help "Specify a custom location for Elm's runtime system."
  } &= help "Quickly reload Elm projects in your browser. Just refresh to recompile.\n\
            \It serves static files and freshly recompiled Elm files."
    &= helpArg [explicit, name "help", name "h"]
    &= versionArg [ explicit, name "version", name "v"
                  , summary "0.12.0.1"
                  ]
    &= summary "Elm Server 0.11.0.1, (c) Evan Czaplicki 2011-2014"

test22 = do
    let Tester{..} = tester "Test22" mode22
    [] === Test22 8000 Nothing
    isVersion ["-v"] "0.12.0.1"
    isVersion ["--version"] "0.12.0.1"
    isVersion ["--numeric-version"] "0.12.0.1"
    isHelp ["--help"] ["Elm Server 0.11.0.1, (c) Evan Czaplicki 2011-2014"]
    isHelp ["--h"] ["Elm Server 0.11.0.1, (c) Evan Czaplicki 2011-2014"]
    fails ["-?"]
    ["--port=20"] === Test22 20 Nothing
    ["--runtime=20"] === Test22 8000 (Just "20")
    fails ["bob"]

-- # 24, doesn't work with Ratio

data Test23 = Test23 {test23A :: Ratio Int} deriving (Show, Data, Typeable, Eq)

mode23 = cmdArgsMode $ Test23 {test23A = 4 % 7 }

test23 = do
    let Tester{..} = tester "Test23" mode23
    [] === Test23 (4 % 7)
    ["--test23=1,6"] === Test23 (1 % 6)


-- For some reason, these must be at the end, otherwise the Template Haskell
-- stage restriction kicks in.

test = test1 >> test2 >> test3 >> test4 >> test5 >> test6 >> test7 >> test8 >> test9 >> test10 >>
       test11 >> test12 >> test13 >> test14 >> test15 >> test16 >> test18 >> test19 >> test20 >>
       test21 >> test22 >> test23
demos = zipWith f [1..]
        [toDemo mode1, toDemo mode2, toDemo mode3, toDemo mode4, toDemo mode5, toDemo mode6
        ,toDemo mode7, toDemo mode8, toDemo mode9, toDemo mode10, toDemo mode11, toDemo mode12
        ,toDemo mode13, toDemo mode14, toDemo mode15, toDemo mode16, toDemo mode17, toDemo mode18
        ,toDemo mode19, toDemo mode20, toDemo mode21, toDemo mode22, toDemo mode23]
    where f i x = x{modeHelp = "Testing various corner cases (" ++ show i ++ ")"}
