module Pos.Infra.Network.Policy
       ( defaultEnqueuePolicyCore
       , defaultEnqueuePolicyRelay
       , defaultEnqueuePolicyEdgeBehindNat
       , defaultEnqueuePolicyEdgeP2P
       , defaultEnqueuePolicyAuxx
       , defaultDequeuePolicyCore
       , defaultDequeuePolicyRelay
       , defaultDequeuePolicyEdgeBehindNat
       , defaultDequeuePolicyAuxx
       , defaultDequeuePolicyEdgeP2P
       , defaultFailurePolicy
       , defaultFailurePolicyAuxx
       ) where


import           Universum

import           Network.Broadcast.OutboundQueue
import           Network.Broadcast.OutboundQueue.Types

-- | Default enqueue policy for core nodes
defaultEnqueuePolicyCore :: EnqueuePolicy nid
defaultEnqueuePolicyCore = go
  where
    go :: EnqueuePolicy nid
    go (MsgAnnounceBlockHeader _) = [
        EnqueueAll NodeCore  (MaxAhead 0) PHighest
      , EnqueueAll NodeRelay (MaxAhead 0) PHighest
      ]
    go (MsgRequestBlockHeaders Nothing) = [
        EnqueueAll NodeCore  (MaxAhead 2) PHigh
      , EnqueueAll NodeRelay (MaxAhead 2) PHigh
      ]
    go (MsgRequestBlockHeaders (Just _)) = [
        -- We never ask for data from edge nodes
        EnqueueOne [NodeRelay, NodeCore] (MaxAhead 3) PHigh
      ]
    go (MsgRequestBlocks _) = [
        -- We never ask for data from edge nodes
        EnqueueOne [NodeRelay, NodeCore] (MaxAhead 3) PHigh
      ]
    go (MsgMPC _) = [
        EnqueueAll NodeCore (MaxAhead 2) PMedium
        -- not sent to relay nodes
      ]
    go (MsgTransaction _) = [
        EnqueueAll NodeCore (MaxAhead 20) PLow
        -- not sent to relay nodes
      ]

-- | Default enqueue policy for relay nodes
defaultEnqueuePolicyRelay :: EnqueuePolicy nid
defaultEnqueuePolicyRelay = go
  where
    -- Enqueue policy for relay nodes
    go :: EnqueuePolicy nid
    go (MsgAnnounceBlockHeader _) = [
        EnqueueAll NodeRelay (MaxAhead 0) PHighest
      , EnqueueAll NodeCore  (MaxAhead 0) PHighest
      , EnqueueAll NodeEdge  (MaxAhead 0) PMedium
      ]
    go (MsgRequestBlockHeaders Nothing) = [
        EnqueueAll NodeCore  (MaxAhead 2) PHigh
      , EnqueueAll NodeRelay (MaxAhead 2) PHigh
      ]
    go (MsgRequestBlockHeaders (Just _)) = [
        -- We never ask for data from edge nodes
        EnqueueOne [NodeRelay, NodeCore] (MaxAhead 3) PHigh
      ]
    go (MsgRequestBlocks _) = [
        -- We never ask for blocks from edge nodes
        EnqueueOne [NodeRelay, NodeCore] (MaxAhead 3) PHigh
      ]
    go (MsgTransaction _) = [
        EnqueueAll NodeCore  (MaxAhead 200) PLow
      , EnqueueAll NodeRelay (MaxAhead 200) PLow
        -- transactions not forwarded to edge nodes
      ]
    go (MsgMPC _) = [
        -- Relay nodes never sent any MPC messages to anyone
      ]

-- | Default enqueue policy for standard behind-NAT edge nodes
defaultEnqueuePolicyEdgeBehindNat :: EnqueuePolicy nid
defaultEnqueuePolicyEdgeBehindNat = go
  where
    -- Enqueue policy for edge nodes
    go :: EnqueuePolicy nid
    go (MsgTransaction OriginSender) = [
        EnqueueAll NodeRelay (MaxAhead 2) PLow
      ]
    go (MsgTransaction (OriginForward _)) = [
        -- don't forward transactions that weren't created at this node
      ]
    go (MsgAnnounceBlockHeader _) = [
        -- not forwarded
      ]
    go (MsgRequestBlockHeaders Nothing) = [
        EnqueueAll NodeRelay (MaxAhead 1) PHigh
      ]
    go (MsgRequestBlockHeaders (Just _)) = [
        EnqueueOne [NodeRelay] (MaxAhead 2) PHigh
      ]
    go (MsgRequestBlocks _) = [
        -- Edge nodes can only talk to relay nodes
        EnqueueOne [NodeRelay] (MaxAhead 2) PHigh
      ]
    go (MsgMPC _) = [
        -- not relevant
      ]

-- | Default enqueue policy for the Auxx CLI tool.
defaultEnqueuePolicyAuxx :: EnqueuePolicy nid
defaultEnqueuePolicyAuxx = go
  where
    -- Enqueue policy for edge nodes
    go :: EnqueuePolicy nid
    go (MsgTransaction OriginSender) = [
        EnqueueAll NodeRelay (MaxAhead 2) PLow
      ]
    go (MsgTransaction (OriginForward _)) = [
        -- don't forward transactions that weren't created at this node
      ]
    go (MsgAnnounceBlockHeader _) = [
        -- not forwarded
      ]
    go (MsgRequestBlockHeaders Nothing) = [
        EnqueueAll NodeRelay (MaxAhead 1) PHigh
      ]
    go (MsgRequestBlockHeaders (Just _)) = [
        EnqueueOne [NodeRelay] (MaxAhead 2) PHigh
      ]
    go (MsgRequestBlocks _) = [
        -- Edge nodes can only talk to relay nodes
        EnqueueOne [NodeRelay] (MaxAhead 2) PHigh
      ]
    go (MsgMPC _) = [
        EnqueueAll NodeCore (MaxAhead 2) PMedium
      , EnqueueAll NodeRelay (MaxAhead 2) PMedium
      ]

-- | Default enqueue policy for edge nodes using P2P
defaultEnqueuePolicyEdgeP2P :: EnqueuePolicy nid
defaultEnqueuePolicyEdgeP2P = go
  where
    -- Enqueue policy for edge nodes
    go :: EnqueuePolicy nid
    go (MsgTransaction _) = [
        EnqueueAll NodeRelay (MaxAhead 3) PLow
      ]
    go (MsgAnnounceBlockHeader _) = [
        EnqueueAll NodeRelay (MaxAhead 0) PHighest
      ]
    go (MsgRequestBlockHeaders _) = [
        EnqueueAll NodeRelay (MaxAhead 2) PHigh
      ]
    go (MsgRequestBlocks _) = [
        -- Edge nodes can only talk to relay nodes
        EnqueueOne [NodeRelay] (MaxAhead 3) PHigh
      ]
    go (MsgMPC _) = [
        -- not relevant
      ]

-- | Default dequeue policy for core nodes
defaultDequeuePolicyCore :: DequeuePolicy
defaultDequeuePolicyCore = go
  where
    go :: DequeuePolicy
    go NodeCore  = Dequeue NoRateLimiting (MaxInFlight 3)
    go NodeRelay = Dequeue NoRateLimiting (MaxInFlight 2)
    go NodeEdge  = error "defaultDequeuePolicy: core to edge not applicable"

-- | Dequeueing policy for relay nodes
defaultDequeuePolicyRelay :: DequeuePolicy
defaultDequeuePolicyRelay = go
  where
    go :: DequeuePolicy
    go NodeCore  = Dequeue (MaxMsgPerSec 3) (MaxInFlight 2)
    go NodeRelay = Dequeue (MaxMsgPerSec 3) (MaxInFlight 2)
    go NodeEdge  = Dequeue (MaxMsgPerSec 1) (MaxInFlight 2)

-- | Dequeueing policy for standard behind-NAT edge nodes
defaultDequeuePolicyEdgeBehindNat :: DequeuePolicy
defaultDequeuePolicyEdgeBehindNat = go
  where
    go :: DequeuePolicy
    go NodeCore  = error "defaultDequeuePolicy: edge to core not applicable"
    go NodeRelay = Dequeue (MaxMsgPerSec 1) (MaxInFlight 2)
    go NodeEdge  = error "defaultDequeuePolicy: edge to edge not applicable"

-- | Dequeueing policy for the Auxx CLI tool.
defaultDequeuePolicyAuxx :: DequeuePolicy
defaultDequeuePolicyAuxx = go
  where
    go :: DequeuePolicy
    go NodeCore  = Dequeue (MaxMsgPerSec 1) (MaxInFlight 2)
    go NodeRelay = Dequeue (MaxMsgPerSec 1) (MaxInFlight 2)
    go NodeEdge  = Dequeue (MaxMsgPerSec 1) (MaxInFlight 2)

-- | Dequeueing policy for P2P edge nodes
defaultDequeuePolicyEdgeP2P :: DequeuePolicy
defaultDequeuePolicyEdgeP2P = go
  where
    go :: DequeuePolicy
    go NodeCore  = error "defaultDequeuePolicy: edge to core not applicable"
    go NodeRelay = Dequeue (MaxMsgPerSec 1) (MaxInFlight 2)
    go NodeEdge  = error "defaultDequeuePolicy: edge to edge not applicable"

-- | Default failure policy
--
-- TODO: Implement proper policy
defaultFailurePolicy :: NodeType -- ^ Our node type
                     -> FailurePolicy nid
defaultFailurePolicy _ourType _theirType _msgType _err = ReconsiderAfter 200

-- | Failure policy for the Auxx CLI tool.
defaultFailurePolicyAuxx :: FailurePolicy nid
defaultFailurePolicyAuxx _ _ _ = ReconsiderAfter 0
