
module Mire.Test.Telnet (telnetTestGroup) where

import Dawn

import qualified Codec.Compression.Zlib as Zlib
import qualified Data.ByteString.Char8 as CS
import qualified Data.ByteString.Lazy.Char8 as CSL
import Data.ByteString.Internal (c2w)

import Mire.Telnet
import Mire.Data.Flow

import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck

import Mire.Test.Base

telnetTestGroup :: TestTree
telnetTestGroup = testGroup "Telnet tests" [
      testCase "Simple text" simpleTest,
      testCase "Zlib substreams" zlibStreamTest,
      testProperty "Random escaped input" randomInputTest,
      testCase "Term type cycle (TTYPE)" termTypeTest,
      testCase "Charset request" charSetTest,
      testCase "SE without SB" wrongSETest,
      testCase "Simple GMCP" gmcpTest
   ]

-- a simple test, what goes in also comes out
simpleTest :: Assertion
simpleTest = assertEqual "" (toNet "some input") (simplePipeTest telnet (toW8 "some input"))

-- this is random input, so escaping IAC is nessecary
randomInputTest :: String -> Bool
randomInputTest s = simplePipeTest telnet (toW8 $ escapeIAC s) == (toNet s)

-- test decoding embedded zlib streams
zlibStreamTest :: Assertion
zlibStreamTest = assertEqual ""
   ((TelnetText <$> toW8 "a ")
      <> [telnetDebug "Enabled COMPRESSv2"]
      <> (toNet "ztest ccc ")
      <> [telnetDebug "Enabled COMPRESSv2"]
      <> (toNet "ztest done"))
   (simplePipeTest telnet (toW8 ("a \255\250\86\255\240" ++ zstring ++ " ccc \255\250\86\255\240" ++ zstring ++ " done")))

zstring :: [Char]
zstring = CSL.unpack $ Zlib.compress $ CSL.pack "ztest"

-- test whether the term type is sent and that it cycles correctly when requested multiple times
termTypeTest :: Assertion
termTypeTest = assertEqual ""
   ( -- [TelnetMeta (MetaDebug "TELNET" "snd IAC SB TERM \"\\NULMXP\" IAC SE")]
     -- <> sent "MXP"
     [telnetDebug "snd IAC SB TERM \"\\NULXTERM\" IAC SE"]
      <> sent "XTERM"
      <> [telnetDebug "snd IAC SB TERM \"\\NULMIRE\" IAC SE"]
      <> sent "MIRE"
      <> [telnetDebug "snd IAC SB TERM \"\\NULUNKNOWN\" IAC SE"]
      <> sent "UNKNOWN"
      <> [telnetDebug "snd IAC SB TERM \"\\NULUNKNOWN\" IAC SE"]
      <> sent "UNKNOWN"
      <> [telnetDebug "snd IAC SB TERM \"\\NULXTERM\" IAC SE"]
      <> sent "XTERM")
   (simplePipeTest telnet (toW8 $ req <> req <> req <> req <> req))
   where req = "\255\250\24\1\255\240" :: String
         sent a = [subSent $ '\24' : '\0' : a]

-- test requesting the supported charset
charSetTest :: Assertion
charSetTest = assertEqual ""
   (fs "test "
    <> [telnetDebug "rcv IAC WILL CHARSET",
        telnetDebug "snd IAC SB CHARSET ECHO \"utf8\" IAC SE"]
    <> [subSent "\42\1utf8"] <> fs " test")
   (simplePipeTest telnet (toW8 "test \255\251\42 test"))

-- test an SE without and SB
wrongSETest :: Assertion
wrongSETest = assertEqual ""
   ([telnetError "Found IAC SE without IAC SB"])
   (simplePipeTest telnet (toW8 $ "\255\240"))

gmcpTest :: Assertion
gmcpTest = assertEqual ""
   ([TelnetMeta (MetaGMCP "{x: a, y: b}")])
   (simplePipeTest telnet (toW8 $ "\255\250\201{x: a, y: b}\255\240"))

-- convert a string to a list of TelnetText elements
fs :: String -> [Telnet]
fs w = TelnetText <$> toW8 w

-- build a MetaSend for a suboption
subSent :: String -> Telnet
subSent a = TelnetMeta $ MetaSend $ CS.pack ("\255\250" <> a <> "\255\240")

-- simple String to [Word8]
toW8 :: String -> [Word8]
toW8 = fmap c2w

toNet :: String -> [Telnet]
toNet = fmap (TelnetText . c2w)

escapeIAC :: String -> String
escapeIAC ('\255':rest) = '\255' : '\255' : escapeIAC rest
escapeIAC (r:rest) = r : escapeIAC rest
escapeIAC [] = []
