{-# LANGUAGE CPP #-}

module Nettle.OpenFlow.Action (
  -- * Actions
  Action (..)
  , ActionType (..)
  , PseudoPort (..)
  , MaxLenToSendController
#if OPENFLOW_VERSION==1
  , VendorID
  , QueueID
#endif
  -- * Action sequences
  , ActionSequence(..)
  , sendOnPort, sendOnInPort, flood, drop, allPhysicalPorts, processNormally, sendToController, processWithTable
  , setVlanVID, setVlanPriority, stripVlanHeader, setEthSrcAddr, setEthDstAddr
  , setIPSrcAddr, setIPDstAddr
#if OPENFLOW_VERSION==152 || OPENFLOW_VERSION==1
  , setIPToS
#endif
  , setTransportSrcPort
  , setTransportDstPort
#if OPENFLOW_VERSION==1    
  , enqueue
  , vendorAction
#endif

  ) where

import Prelude hiding (drop)
import Nettle.OpenFlow.Port
import Nettle.Ethernet.EthernetAddress
import Nettle.Ethernet.EthernetFrame
import Nettle.IPv4.IPAddress
import Nettle.IPv4.IPPacket
import Data.Word


-- |The supported switch actions are denoted with these symbols.
data ActionType = OutputToPortType    
                | SetVlanVIDType      
                | SetVlanPriorityType 
                | StripVlanHeaderType 
                | SetEthSrcAddrType   
                | SetEthDstAddrType   
                | SetIPSrcAddrType    
                | SetIPDstAddrType    
#if OPENFLOW_VERSION==152 || OPENFLOW_VERSION==1
                | SetIPTypeOfServiceType        
#endif
                | SetTransportSrcPortType
                | SetTransportDstPortType
#if OPENFLOW_VERSION==1
                | EnqueueType            
#endif
                | VendorActionType
                  deriving (Show,Read,Eq,Ord,Enum)

-- | Each flow table entry contains a list of actions that will
-- be executed when a packet matches the entry. 
-- Specification: @ofp_action_header@ and all @ofp_action_*@ structures.
data Action
    = SendOutPort PseudoPort        -- ^send out given port
    | SetVlanVID VLANID             -- ^set the 802.1q VLAN ID
    | SetVlanPriority VLANPriority  -- ^set the 802.1q priority
    | StripVlanHeader               -- ^strip the 802.1q header
    | SetEthSrcAddr EthernetAddress -- ^set ethernet source address
    | SetEthDstAddr EthernetAddress -- ^set ethernet destination address
    | SetIPSrcAddr IPAddress        -- ^set IP source address
    | SetIPDstAddr IPAddress        -- ^set IP destination address
#if OPENFLOW_VERSION==152 || OPENFLOW_VERSION==1
    | SetIPToS IPTypeOfService      -- ^IP ToS (DSCP field)
#endif
    | SetTransportSrcPort TransportPort -- ^set TCP/UDP source port
    | SetTransportDstPort TransportPort -- ^set TCP/UDP destination port
#if OPENFLOW_VERSION==1    
    | Enqueue {
        enqueuePort :: PortID,       -- ^port the queue belongs to
        queueID     :: QueueID       -- ^where to enqueue the packets
      } -- ^output to queue
    | VendorAction VendorID [Word8] 
#endif
    deriving (Show,Eq)
           

-- | A @PseudoPort@ denotes the target of a forwarding
-- action. 
data PseudoPort = PhysicalPort PortID                 -- ^send out physical port with given id
                | InPort                              -- ^send packet out the input port
                | Flood                               -- ^send out all physical ports except input port and those disabled by STP
                | AllPhysicalPorts                    -- ^send out all physical ports except input port
                | ToController MaxLenToSendController -- ^send to controller
                | NormalSwitching                     -- ^process with normal L2/L3 switching
                | WithTable                           -- ^process packet with flow table
                  deriving (Show,Read, Eq)

-- | A send to controller action includes the maximum
-- number of bytes that a switch will send to the 
-- controller.
type MaxLenToSendController = Word16

#if OPENFLOW_VERSION==1
type VendorID = Word32
type QueueID  = Word32
#endif
       
-- | Sequence of actions, represented as finite lists. The Monoid instance of
-- lists provides methods for denoting the do-nothing action (@mempty@) and for concatenating action sequences @mconcat@. 
type ActionSequence = [Action]

-- | send p is a packet send action.
send :: PseudoPort -> ActionSequence
send p = [SendOutPort p]

sendOnPort :: PortID -> ActionSequence
sendOnPort p = [SendOutPort $ PhysicalPort p]

sendOnInPort, flood, drop, allPhysicalPorts, processNormally :: ActionSequence
sendOnInPort = send InPort
flood = send Flood
drop  = []
allPhysicalPorts = send AllPhysicalPorts
processNormally = send NormalSwitching
processWithTable = send WithTable

sendToController :: MaxLenToSendController -> ActionSequence
sendToController maxlen = send (ToController maxlen)

setVlanVID :: VLANID -> ActionSequence
setVlanVID vlanid = [SetVlanVID vlanid]

setVlanPriority :: VLANPriority -> ActionSequence
setVlanPriority x = [SetVlanPriority x]

stripVlanHeader :: ActionSequence
stripVlanHeader = [StripVlanHeader]

setEthSrcAddr :: EthernetAddress -> ActionSequence
setEthSrcAddr addr = [SetEthSrcAddr addr]

setEthDstAddr :: EthernetAddress -> ActionSequence
setEthDstAddr addr = [SetEthDstAddr addr]

setIPSrcAddr ::  IPAddress -> ActionSequence
setIPSrcAddr addr = [SetIPSrcAddr addr]

setIPDstAddr ::  IPAddress -> ActionSequence
setIPDstAddr addr = [SetIPDstAddr addr]

#if OPENFLOW_VERSION==152 || OPENFLOW_VERSION==1
setIPToS :: IPTypeOfService -> ActionSequence
setIPToS tos = [SetIPToS tos]
#endif

setTransportSrcPort ::  TransportPort -> ActionSequence
setTransportSrcPort port = [SetTransportSrcPort port]

setTransportDstPort ::  TransportPort -> ActionSequence
setTransportDstPort port = [SetTransportDstPort port]


#if OPENFLOW_VERSION==1    
enqueue :: PortID -> QueueID -> ActionSequence
enqueue portid queueid = [Enqueue portid queueid]    

vendorAction :: VendorID -> [Word8] -> ActionSequence
vendorAction vid bytes = [VendorAction vid bytes]
#endif