{-# LANGUAGE OverloadedStrings, CPP #-}

module DecodeSpec where

import Data.ByteString.Internal (ByteString(..), unsafeCreate)
#if !MIN_VERSION_bytestring(0,10,0)
import qualified Data.ByteString as BS
#endif
import qualified Data.ByteString.Lazy as BL
import Data.Word8
import Foreign.ForeignPtr (withForeignPtr)
import Foreign.Ptr (plusPtr)
import Foreign.Storable (peek, poke, peekByteOff)
import Network.DNS.Decode
import Network.DNS.Encode
import Test.Hspec

----------------------------------------------------------------

test_doublePointer :: BL.ByteString
test_doublePointer = "f7eb8500000100010007000404736563330561706e696303636f6d0000010001c00c0001000100001c200004ca0c1c8cc0110002000100001c20000f036e73310561706e6963036e657400c0300002000100001c200006036e7333c040c0300002000100001c200006036e7334c040c0300002000100001c20001004736563310561706e696303636f6d00c0300002000100001c20001704736563310761757468646e730472697065036e657400c0300002000100001c20001004736563320561706e696303636f6d00c0300002000100001c2000070473656333c0bfc07b0001000100001c200004ca0c1d3bc07b001c000100001c20001020010dc02001000a4608000000000059c0ba0001000100001c200004ca0c1d3cc0d6001c000100001c20001020010dc0000100004777000000000140"

test_txt :: BL.ByteString
test_txt = "463181800001000100000000076e69636f6c6173046b766462076e647072696d6102696f0000100001c00c0010000100000e10000c6e69636f6c61732e6b766462"

test_dname :: BL.ByteString
test_dname = "b3c0818000010005000200010377777706376b616e616c02636f02696c0000010001c0100027000100000003000c0769737261656c3702727500c00c0005000100000003000603777777c02ec046000500010000255b0002c02ec02e000100010000003d000451daf938c02e000100010000003d0004c33ce84ac02e000200010005412b000c036e7332026137036f726700c02e000200010005412b0006036e7331c08a0000291000000000000000"

----------------------------------------------------------------

spec :: Spec
spec = do
    describe "decode" $ do
        it "decodes double pointers correctly" $ do
            let Right x1 = decode $ fromHexString test_doublePointer
                Right x2 = decode (encode x1)
                Right x3 = decode (encode x2)
            x3 `shouldBe` x2
        it "decodes dname" $ do
            let Right x1 = decode $ fromHexString test_dname
                Right x2 = decode (encode x1)
                Right x3 = decode (encode x2)
            print x1
            x3 `shouldBe` x2
        it "decodes txt" $ do
            let Right x1 = decode $ fromHexString test_txt
                Right x2 = decode (encode x1)
                Right x3 = decode (encode x2)
            print x1
            x3 `shouldBe` x2

----------------------------------------------------------------

fromHexString :: BL.ByteString -> BL.ByteString
#if MIN_VERSION_bytestring(0,10,0)
fromHexString = BL.fromStrict . fromHexString' . BL.toStrict
#else
fromHexString = BL.pack . BS.unpack . fromHexString' . BS.pack . BL.unpack
#endif

fromHexString' :: ByteString -> ByteString
fromHexString' (PS fptr off len) = unsafeCreate size $ \dst ->
    withForeignPtr fptr $ \src -> go (src `plusPtr` off) dst 0
  where
    size = len `div` 2
    go from to bytes
      | bytes == size = return ()
      | otherwise    = do
          w1 <- peek from
          w2 <- peekByteOff from 1
          let w = hex2w (w1,w2)
          poke to w
          go (from `plusPtr` 2) (to `plusPtr` 1) (bytes + 1)

hex2w :: (Word8, Word8) -> Word8
hex2w (w1,w2) = h2w w1 * 16 + h2w w2

h2w :: Word8 -> Word8
h2w w
  | isDigit w = w - _0
  | otherwise = w - _a + 10
