-- | Domain-specific Hedgehog generators.
--
-- These are used in multiple test modules, so broken out here for clearer re-use.
module Generator
  ( clientMessages
  , serverMessages
  , appIDs
  , phases
  , sides
  , offerMessages
  ) where

import Protolude

import Hedgehog (MonadGen(..))
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range

import MagicWormhole.Internal.Messages
  ( ClientMessage(..)
  , ServerMessage(..)
  , MailboxMessage(..)
  , WelcomeMessage(..)
  , AppID(..)
  , Side(..)
  , MessageID(..)
  , Phase(..)
  , Body(..)
  , Nameplate(..)
  , Mailbox(..)
  , Mood(..)
  )
import qualified MagicWormhole.Internal.FileTransfer as F
  ( Offer(..)
  , DirectoryMode(..)
  )

clientMessages :: MonadGen m => m ClientMessage
clientMessages = Gen.choice
  [ Bind <$> appIDs <*> sides
  , pure List
  , pure Allocate
  , Claim <$> genNameplates
  , Release <$> Gen.maybe genNameplates
  , Open <$> mailboxes
  , Add <$> phases <*> bodies
  , Close <$> Gen.maybe mailboxes <*> Gen.maybe moods
  , Ping <$> Gen.int Range.linearBounded
  ]

messageIDs :: MonadGen m => m MessageID
messageIDs = MessageID <$> Gen.int16 (Range.linear 0 maxBound)

appIDs :: MonadGen m => m AppID
appIDs = AppID <$> Gen.choice [ Gen.text (Range.linear 0 100) Gen.unicode
                              , Gen.element
                                [ "lothar.com/wormhole/text-or-file-xfer"
                                , "tahoe-lafs.org/tahoe-lafs/v1"
                                ]
                              ]

sides :: MonadGen m => m Side
sides = Side <$> Gen.text (Range.linear 0 10) Gen.hexit

phases :: MonadGen m => m Phase
phases = Gen.choice
  [ pure PakePhase
  , pure VersionPhase
  , ApplicationPhase <$> Gen.int (Range.linear 0 maxBound)
  ]

bodies :: MonadGen m => m Body
bodies = Body <$> Gen.bytes (Range.linear 0 1024)

genNameplates :: MonadGen m => m Nameplate
genNameplates = Nameplate <$> Gen.text (Range.linear 0 10) Gen.unicode

mailboxes :: MonadGen m => m Mailbox
mailboxes = Mailbox <$> Gen.text (Range.singleton 13) alphaNum
  where
    alphaNum = Gen.element "abcdefghijklmnopqrstuvwxyz09123456789"

moods :: MonadGen m => m Mood
moods = Gen.element [ Happy, Lonely, Scary, Errory ]

serverMessages :: MonadGen m => m ServerMessage
serverMessages = Gen.choice
  [ Welcome <$> welcomeMessages
  , Nameplates <$> Gen.list (Range.linear 0 10) genNameplates
  , Allocated <$> genNameplates
  , Claimed <$> mailboxes
  , pure Released
  , Message <$> mailboxMessages
  , pure Closed
  , pure Ack
  , Pong <$> Gen.int Range.linearBounded
  , Error <$> Gen.text (Range.linear 0 100) Gen.unicode <*> clientMessages
  ]

mailboxMessages :: MonadGen m => m MailboxMessage
mailboxMessages = MailboxMessage <$> sides <*> phases <*> Gen.maybe messageIDs <*> bodies

welcomeMessages :: MonadGen m => m WelcomeMessage
welcomeMessages = WelcomeMessage <$> Gen.maybe (Gen.text (Range.linear 0 1024) Gen.unicode) <*> Gen.maybe (Gen.text (Range.linear 0 1024) Gen.unicode)

directoryModes :: MonadGen m => m F.DirectoryMode
directoryModes = Gen.choice
  [ return F.ZipFileDeflated
  ]

offerMessages :: MonadGen m => m F.Offer
offerMessages = Gen.choice
  [ F.Message <$> (Gen.text (Range.linear 0 1024) Gen.unicode)
  , F.File <$> toS <$> (Gen.text (Range.linear 0 100) Gen.unicode)  <*> (fromIntegral <$> (Gen.int (Range.linear 0 maxBound)))
  , F.Directory
    <$> directoryModes -- (toS <$> (Gen.text (Range.linear 0 100) Gen.unicode))
    <*> (toS <$> (Gen.text (Range.linear 0 100) Gen.unicode))
    <*> (fromIntegral <$> (Gen.int (Range.linear 0 maxBound)))
    <*> (fromIntegral <$> (Gen.int (Range.linear 0 maxBound)))
    <*> (fromIntegral <$> (Gen.int (Range.linear 0 maxBound)))
  ]
