{-# LINE 1 "Network/Socket/Msg/CMsg.hsc" #-}
{-# LANGUAGE CPP,ScopedTypeVariables #-}
{-# LINE 2 "Network/Socket/Msg/CMsg.hsc" #-}
module Network.Socket.Msg.CMsg
    ( CMsg(..)
    , CMsgable(..)
    , filterCMsgs

{-# LINE 7 "Network/Socket/Msg/CMsg.hsc" #-}
    , IpPktInfo(..)

{-# LINE 9 "Network/Socket/Msg/CMsg.hsc" #-}
    ) where


{-# LINE 12 "Network/Socket/Msg/CMsg.hsc" #-}

{-# LINE 13 "Network/Socket/Msg/CMsg.hsc" #-}

import Control.Applicative
import Data.Binary
import Data.Binary.Get (getWord32host)
import Data.Binary.Put (putWord32host)
import qualified Data.ByteString as B
import Data.ByteString.Lazy (fromStrict,toStrict)
import Network.Socket (HostAddress)

data CMsg = CMsg
    { cmsgLevel :: !Int
    , cmsgType  :: !Int
    , cmsgData  :: !B.ByteString
    }

instance Show CMsg where
    show cmsg = concat ["(",
                        "Level: ", show $ cmsgLevel cmsg, ", ",
                        "Type: ", show $ cmsgType cmsg, ", ",
                        "Data: ", show $ cmsgData cmsg, ")"]

-- |Class for binary structures that can be used as control messages (cmsg(3)).
-- 
-- Complete definition requires for a type to be an instance of Binary class,
-- as well as to provide getCMsgLevel and getCMsgType methods.
--
-- Note that the argument of getCMsgLevel and getCMsgType methods should not
-- be used as it might be undefined.
class Binary a => CMsgable a where
    getCMsgLevel    :: a -> Int
    getCMsgType     :: a -> Int

    toCMsg :: a -> CMsg
    toCMsg x = CMsg { cmsgLevel = getCMsgLevel x
                    , cmsgType = getCMsgType x
                    , cmsgData = toStrict $ encode x }

    -- XXX: Find a way to check type and level values in here
    fromCMsg :: CMsg -> Maybe a
    fromCMsg cmsg = case decodeOrFail (fromStrict $ cmsgData cmsg) of
                        Left _ -> Nothing
                        Right (_,_,x) -> Just x

-- |Filter specific kind of control messages.
-- 
-- Example: filterCMsgs (undefined :: IpPktInfo) cmsgs
filterCMsgs :: (CMsgable a) => a -> [CMsg] -> [CMsg]
filterCMsgs x = filter $ \c -> (cmsgType c == getCMsgType x) && (cmsgLevel c == getCMsgLevel x)


{-# LINE 63 "Network/Socket/Msg/CMsg.hsc" #-}
data IpPktInfo = IpPktInfo
    { ipi_ifindex   :: !Word32
    , ipi_spec_dst  :: !HostAddress
    , ipi_addr      :: !HostAddress
    } deriving (Show)

instance Binary IpPktInfo where
    put i = do
        -- XXX: Assume that sizeof(int) == 4
        putWord32host $ ipi_ifindex i
        putWord32host $ ipi_spec_dst i
        putWord32host $ ipi_addr i
    get = IpPktInfo <$> getWord32host
                    <*> getWord32host
                    <*> getWord32host

instance CMsgable IpPktInfo where
    getCMsgLevel    _ = 0
{-# LINE 81 "Network/Socket/Msg/CMsg.hsc" #-}
    getCMsgType     _ = 8
{-# LINE 82 "Network/Socket/Msg/CMsg.hsc" #-}


{-# LINE 84 "Network/Socket/Msg/CMsg.hsc" #-}