{-|
  Arbitrary types for Network.Haskoin.Node
-}
module Network.Haskoin.Test.Node
( ArbitraryVarInt(..)
, ArbitraryVarString(..)
, ArbitraryNetworkAddress(..)
, ArbitraryNetworkAddressTime(..)
, ArbitraryInvType(..)
, ArbitraryInvVector(..)
, ArbitraryInv(..)
, ArbitraryVersion(..)
, ArbitraryAddr(..)
, ArbitraryAlert(..)
, ArbitraryReject(..)
, ArbitraryRejectCode(..)
, ArbitraryGetData(..)
, ArbitraryNotFound(..)
, ArbitraryPing(..)
, ArbitraryPong(..)
, ArbitraryBloomFlags(..)
, ArbitraryBloomFilter(..)
, ArbitraryFilterLoad(..)
, ArbitraryFilterAdd(..)
, ArbitraryMessageCommand(..)
) where

import Test.QuickCheck
    ( Arbitrary
    , arbitrary
    , elements
    , listOf1
    , oneof
    , choose
    , vectorOf
    )

import Data.Word (Word16, Word32)
import qualified Data.ByteString as BS (pack, empty)

import Network.Socket (SockAddr(..))

import Network.Haskoin.Test.Crypto
import Network.Haskoin.Node

-- | Arbitrary VarInt
newtype ArbitraryVarInt = ArbitraryVarInt VarInt
    deriving (Eq, Show, Read)

instance Arbitrary ArbitraryVarInt where
    arbitrary = ArbitraryVarInt . VarInt <$> arbitrary

-- | Arbitrary VarString
newtype ArbitraryVarString = ArbitraryVarString VarString
    deriving (Eq, Show, Read)

instance Arbitrary ArbitraryVarString where
    arbitrary = do
        ArbitraryByteString bs <- arbitrary
        return $ ArbitraryVarString $ VarString bs

-- | Arbitrary NetworkAddress
newtype ArbitraryNetworkAddress = ArbitraryNetworkAddress NetworkAddress
    deriving (Eq, Show)

instance Arbitrary ArbitraryNetworkAddress where
    arbitrary = do
        s <- arbitrary
        a <- arbitrary
        p <- arbitrary
        ArbitraryNetworkAddress . (NetworkAddress s) <$> oneof
            [ do
                b <- arbitrary
                c <- arbitrary
                d <- arbitrary
                return $ SockAddrInet6 (fromIntegral p) 0 (a,b,c,d) 0
            , return $ SockAddrInet (fromIntegral (p :: Word16)) a
            ]

-- | Arbitrary NetworkAddressTime
newtype ArbitraryNetworkAddressTime
    = ArbitraryNetworkAddressTime (Word32, NetworkAddress)

instance Arbitrary ArbitraryNetworkAddressTime where
    arbitrary = do
        w <- arbitrary
        ArbitraryNetworkAddress a <- arbitrary
        return $ ArbitraryNetworkAddressTime (w,a)

-- | Arbitrary InvType
newtype ArbitraryInvType = ArbitraryInvType InvType
    deriving (Eq, Show, Read)

instance Arbitrary ArbitraryInvType where
    arbitrary = ArbitraryInvType <$> elements
        [InvError, InvTx, InvBlock, InvMerkleBlock]

-- | Arbitrary InvVector
newtype ArbitraryInvVector = ArbitraryInvVector InvVector
    deriving (Eq, Show, Read)

instance Arbitrary ArbitraryInvVector where
    arbitrary = do
        ArbitraryInvType t <- arbitrary
        ArbitraryHash256 h <- arbitrary
        return $ ArbitraryInvVector $ InvVector t h

-- | Arbitrary non-empty Inv
newtype ArbitraryInv = ArbitraryInv Inv
    deriving (Eq, Show, Read)

instance Arbitrary ArbitraryInv where
    arbitrary = do
        vs <- listOf1 arbitrary
        return $ ArbitraryInv $ Inv $ map (\(ArbitraryInvVector v) -> v) vs

-- | Arbitrary Version
newtype ArbitraryVersion = ArbitraryVersion Version
    deriving (Eq, Show)

instance Arbitrary ArbitraryVersion where
    arbitrary = do
        v <- arbitrary
        s <- arbitrary
        t <- arbitrary
        ArbitraryNetworkAddress nr <- arbitrary
        ArbitraryNetworkAddress ns <- arbitrary
        n <- arbitrary
        ArbitraryVarString a <- arbitrary
        h <- arbitrary
        r <- arbitrary
        return $ ArbitraryVersion $ Version v s t nr ns n a h r

-- | Arbitrary non-empty Addr
newtype ArbitraryAddr = ArbitraryAddr Addr
    deriving (Eq, Show)

instance Arbitrary ArbitraryAddr where
    arbitrary = do
       vs <- listOf1 arbitrary
       return $ ArbitraryAddr $ Addr $
           map (\(ArbitraryNetworkAddressTime x) -> x) vs

-- | Arbitrary alert with random payload and signature. Signature is not
-- valid.
newtype ArbitraryAlert = ArbitraryAlert Alert
    deriving (Eq, Show, Read)

instance Arbitrary ArbitraryAlert where
    arbitrary = do
        ArbitraryVarString p <- arbitrary
        ArbitraryVarString s <- arbitrary
        return $ ArbitraryAlert $ Alert p s

-- | Arbitrary Reject
newtype ArbitraryReject = ArbitraryReject Reject
    deriving (Eq, Show, Read)

instance Arbitrary ArbitraryReject where
    arbitrary = do
        ArbitraryMessageCommand m <- arbitrary
        ArbitraryRejectCode c <- arbitrary
        ArbitraryVarString s <- arbitrary
        d <- oneof [ return BS.empty
                   , BS.pack <$> vectorOf 32 arbitrary
                   ]
        return $ ArbitraryReject $ Reject m c s d

-- | Arbitrary RejectCode
newtype ArbitraryRejectCode = ArbitraryRejectCode RejectCode
    deriving (Eq, Show, Read)

instance Arbitrary ArbitraryRejectCode where
    arbitrary = ArbitraryRejectCode <$> elements
        [ RejectMalformed
        , RejectInvalid
        , RejectInvalid
        , RejectDuplicate
        , RejectNonStandard
        , RejectDust
        , RejectInsufficientFee
        , RejectCheckpoint
        ]

-- | Arbitrary non-empty GetData
newtype ArbitraryGetData = ArbitraryGetData GetData
    deriving (Eq, Show, Read)

instance Arbitrary ArbitraryGetData where
    arbitrary = do
        vs <- listOf1 arbitrary
        return $ ArbitraryGetData $ GetData $
            map (\(ArbitraryInvVector x) -> x) vs

-- | Arbitrary NotFound
newtype ArbitraryNotFound = ArbitraryNotFound NotFound
    deriving (Eq, Show, Read)

instance Arbitrary ArbitraryNotFound where
    arbitrary = do
        vs <- listOf1 arbitrary
        return $ ArbitraryNotFound $ NotFound $
            map (\(ArbitraryInvVector x) -> x) vs

-- | Arbitrary Ping
newtype ArbitraryPing = ArbitraryPing Ping
    deriving (Eq, Show, Read)

instance Arbitrary ArbitraryPing where
    arbitrary = ArbitraryPing . Ping <$> arbitrary

-- | Arbitrary Pong
newtype ArbitraryPong = ArbitraryPong Pong
    deriving (Eq, Show, Read)

instance Arbitrary ArbitraryPong where
    arbitrary = ArbitraryPong . Pong <$> arbitrary

-- | Arbitrary bloom filter flags
data ArbitraryBloomFlags = ArbitraryBloomFlags BloomFlags
    deriving (Eq, Show, Read)

instance Arbitrary ArbitraryBloomFlags where
    arbitrary = ArbitraryBloomFlags <$> elements
        [ BloomUpdateNone
        , BloomUpdateAll
        , BloomUpdateP2PubKeyOnly
        ]

-- | Arbitrary bloom filter with its corresponding number of elements
-- and false positive rate.
data ArbitraryBloomFilter = ArbitraryBloomFilter Int Double BloomFilter
    deriving (Eq, Show, Read)

instance Arbitrary ArbitraryBloomFilter where
    arbitrary = do
        n     <- choose (0,100000)
        fp    <- choose (1e-8,1)
        tweak <- arbitrary
        ArbitraryBloomFlags fl <- arbitrary
        return $ ArbitraryBloomFilter n fp $ bloomCreate n fp tweak fl

-- | Arbitrary FilterLoad
data ArbitraryFilterLoad = ArbitraryFilterLoad FilterLoad
    deriving (Eq, Show, Read)

instance Arbitrary ArbitraryFilterLoad where
    arbitrary = do
        ArbitraryBloomFilter _ _ bf <- arbitrary
        return $ ArbitraryFilterLoad $ FilterLoad bf

-- | Arbitrary FilterAdd
data ArbitraryFilterAdd = ArbitraryFilterAdd FilterAdd
    deriving (Eq, Show, Read)

instance Arbitrary ArbitraryFilterAdd where
    arbitrary = do
        ArbitraryByteString bs <- arbitrary
        return $ ArbitraryFilterAdd $ FilterAdd bs


-- | Arbitrary MessageCommand
newtype ArbitraryMessageCommand = ArbitraryMessageCommand MessageCommand
    deriving (Eq, Show, Read)

instance Arbitrary ArbitraryMessageCommand where
    arbitrary = ArbitraryMessageCommand <$> elements
        [ MCVersion
        , MCVerAck
        , MCAddr
        , MCInv
        , MCGetData
        , MCNotFound
        , MCGetBlocks
        , MCGetHeaders
        , MCTx
        , MCBlock
        , MCMerkleBlock
        , MCHeaders
        , MCGetAddr
        , MCFilterLoad
        , MCFilterAdd
        , MCFilterClear
        , MCPing
        , MCPong
        , MCAlert
        ]