{-# LANGUAGE ForeignFunctionInterface #-}

-- | Interface to the @select(2)@ POSIX function.
module System.Posix.IO.Select (select, Timeout(Never, Time)) where

import System.Posix.IO.Select.Types
import Foreign
import Foreign.C.Types
import System.Posix.Types
import qualified Data.Vector.Storable as V -- For contiguous memory and passing to C. Should perhaps be an Array instead.

foreign import ccall "select_wrapper.h select_wrapper"
        c_select_wrapper :: Ptr CInt -> CInt -> 
                            Ptr CInt -> CInt -> 
                            Ptr CInt -> CInt ->
                            CChar -> CLong -> CLong -> 
                            IO CInt

-- | @'select' readFds writeFds exceptFds timeout@ calls the
-- @select(2)@ function with the file descriptors in @readFds@ as the
-- FD set to watch for read readiness, and similarly for @writeFds@
-- and @exceptFds@, with @timeout@ specifying the timeout. The return
-- value is that of the call.
select :: [Fd] -> [Fd] -> [Fd] -> Timeout -> IO CInt
select readFds writeFds exceptFds timeout =
    let
        rfds = V.fromList (map fromIntegral readFds) :: V.Vector CInt -- Make contiguous memory.
        nr = fromIntegral (V.length rfds)
        wfds = V.fromList (map fromIntegral writeFds) :: V.Vector CInt
        nw = fromIntegral (V.length wfds)
        efds = V.fromList (map fromIntegral exceptFds) :: V.Vector CInt
        ne = fromIntegral (V.length efds)
        (tFlag, s, us) = timeoutToC timeout
    in
      -- Yikes, the next line is ugly. But stare at unsafeWith, and you'll see this is right.
      V.unsafeWith rfds $ \pr -> V.unsafeWith wfds $ \pw -> V.unsafeWith efds $ \pe -> 
      c_select_wrapper pr nr pw nw pe ne tFlag s us 

timeoutToC :: Timeout -> (CChar, CLong, CLong)
timeoutToC Never = (0, 0, 0)
timeoutToC (Time s us) = (1, fromIntegral s, fromIntegral us)