{-# LINE 1 "Network/Socket/Msg/CMsg.hsc" #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LINE 2 "Network/Socket/Msg/CMsg.hsc" #-}

module Network.Socket.Msg.CMsg
    ( CSockLen
    , CMsg(..)
    , CMsgHdr(..)
    , c_cmsg_firsthdr
    , c_cmsg_nexthdr
    , c_cmsg_data
    , cmsgSpace
    , peekCMsg
    , pokeCMsg
    ) where


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

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

import Network.Socket.Msg.MsgHdr (MsgHdr)

import qualified Data.ByteString as B
import Data.Maybe (isNothing,fromJust)
import Foreign.C.Types (CUInt(..),CInt(..),CSize(..))
import Foreign.Marshal.Utils (copyBytes)
import Foreign.Ptr (Ptr,castPtr,nullPtr)
import Foreign.Storable (Storable(..))

type CSockLen = CUInt   -- The way it is defined somewhere in bits/types.h

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, ")"]

data CMsgHdr = CMsgHdr
    { cmsgLen       :: CSockLen
    , cmsghdrLevel  :: CInt
    , cmsghdrType   :: CInt
    }

instance Storable CMsgHdr where
    sizeOf _ = (16)
{-# LINE 49 "Network/Socket/Msg/CMsg.hsc" #-}
    alignment _ = alignment (undefined :: CInt)

    peek p = do
        len <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) p
{-# LINE 53 "Network/Socket/Msg/CMsg.hsc" #-}
        level <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) p
{-# LINE 54 "Network/Socket/Msg/CMsg.hsc" #-}
        t <- ((\hsc_ptr -> peekByteOff hsc_ptr 12)) p
{-# LINE 55 "Network/Socket/Msg/CMsg.hsc" #-}
        return $ CMsgHdr len level t

    poke p cmh = do
        ((\hsc_ptr -> pokeByteOff hsc_ptr 0)) p (cmsgLen cmh)
{-# LINE 59 "Network/Socket/Msg/CMsg.hsc" #-}
        ((\hsc_ptr -> pokeByteOff hsc_ptr 8)) p (cmsghdrLevel cmh)
{-# LINE 60 "Network/Socket/Msg/CMsg.hsc" #-}
        ((\hsc_ptr -> pokeByteOff hsc_ptr 12)) p (cmsghdrType cmh)
{-# LINE 61 "Network/Socket/Msg/CMsg.hsc" #-}

-- The manual says the following functions (actually macros)
-- are constant and thus we do not have to use IO monad.

foreign import ccall unsafe "cmsg_firsthdr"
  c_cmsg_firsthdr :: Ptr MsgHdr -> Ptr CMsgHdr

foreign import ccall unsafe "cmsg_nexthdr"
  c_cmsg_nexthdr :: Ptr MsgHdr -> Ptr CMsgHdr -> Ptr CMsgHdr

foreign import ccall unsafe "cmsg_data"
  c_cmsg_data :: Ptr CMsgHdr -> Ptr ()

foreign import ccall unsafe "cmsg_space"
  c_cmsg_space :: CSize -> CSize

cmsgSpace :: CMsg -> Int
cmsgSpace = spc . B.length . cmsgData
    where spc = fromIntegral . c_cmsg_space . fromIntegral

cmsgExtractData :: Ptr CMsgHdr -> IO (Maybe B.ByteString)
cmsgExtractData p = do
    let dataPtr = castPtr $ c_cmsg_data p
    dataLen <- return . cmsgLen =<< peek p
    if dataPtr == nullPtr
        then return Nothing
        else return.Just =<< B.packCStringLen (dataPtr, fromIntegral dataLen)

peekCMsg :: Ptr CMsgHdr -> IO (Maybe CMsg)
peekCMsg pCMsgHdr =
        peek pCMsgHdr >>= \cmsghdr ->
        cmsgExtractData pCMsgHdr >>= \dat ->
        return $ if isNothing dat
            then Nothing
            else Just CMsg { cmsgLevel = fromIntegral $ cmsghdrLevel cmsghdr
                           , cmsgType = fromIntegral $ cmsghdrType cmsghdr
                           , cmsgData = fromJust dat }

pokeCMsg :: Ptr CMsgHdr -> CMsg -> IO ()
pokeCMsg pHdr cmsg = do
        poke pHdr cmsghdr
        let dptr = castPtr $ c_cmsg_data pHdr
        B.useAsCStringLen (cmsgData cmsg) $ \(bptr,len) -> copyBytes dptr bptr len
    where
        cmsghdr = CMsgHdr { cmsgLen = fromIntegral $ B.length $ cmsgData cmsg
                          , cmsghdrLevel = fromIntegral $ cmsgLevel cmsg
                          , cmsghdrType = fromIntegral $ cmsgType cmsg }