{-# LINE 1 "Network/Bluetooth/Device.hsc" #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LINE 2 "Network/Bluetooth/Device.hsc" #-}
module Network.Bluetooth.Device (
        Device(..),
        deviceName,
        RFCOMMSocket,
        openRFCOMM,
        recvRFCOMM,
        sendRFCOMM,
        sendAllRFCOMM,
        closeRFCOMM,
        module Network.Bluetooth.Types
    ) where


{-# LINE 17 "Network/Bluetooth/Device.hsc" #-}

{-# LINE 18 "Network/Bluetooth/Device.hsc" #-}

{-# LINE 19 "Network/Bluetooth/Device.hsc" #-}

{-# LINE 20 "Network/Bluetooth/Device.hsc" #-}

{-# LINE 21 "Network/Bluetooth/Device.hsc" #-}

{-# LINE 22 "Network/Bluetooth/Device.hsc" #-}

import Network.Bluetooth.Types

{-# LINE 27 "Network/Bluetooth/Device.hsc" #-}
import Network.Socket
import qualified Network.Socket.ByteString as NB

import Control.Applicative
import Control.Exception
import Control.Monad
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as B
import qualified Data.ByteString.Unsafe as B
import Data.IORef
import Data.List (lookup)
import Data.Word
import Foreign
import Foreign.C
import GHC.Conc (threadWaitWrite)

{-# LINE 46 "Network/Bluetooth/Device.hsc" #-}


data Device = Device Adapter BluetoothAddr deriving (Eq, Ord, Show)

foreign import ccall safe "hci_read_remote_name" hci_read_remote_name
    :: CInt -> Ptr BluetoothAddr -> CInt -> Ptr CChar -> CInt -> IO CInt

deviceName :: Device -> IO ByteString

{-# LINE 65 "Network/Bluetooth/Device.hsc" #-}
deviceName dev@(Device (Adapter _ dd) (BluetoothAddr bs)) = do
    retRef <- newIORef 0
    bs0 <- B.create maxLen $ \buf -> do
        ret <- B.unsafeUseAsCString bs $ \cs ->
            hci_read_remote_name dd (castPtr cs) (fromIntegral maxLen) (castPtr buf) 0
        writeIORef retRef ret
    ret <- readIORef retRef
    if ret < 0 then do
        errno@(Errno errno_) <- getErrno
        if errno == eINTR
            then deviceName dev
            else do
                err <- peekCString (strerror errno_)
                throwIO $ BluetoothException "deviceName" err
      else
        return $ B.takeWhile (/= 0) bs0
  where
    maxLen = 255

{-# LINE 84 "Network/Bluetooth/Device.hsc" #-}

data RFCOMMSocket = RFCOMMSocket Socket


{-# LINE 88 "Network/Bluetooth/Device.hsc" #-}
data SockAddrBTH = SockAddrBTH Word16 ByteString Word8

sockAddrBTH :: BluetoothAddr -> Word8 -> SockAddrBTH
sockAddrBTH (BluetoothAddr bs) port = SockAddrBTH (31) bs port
{-# LINE 92 "Network/Bluetooth/Device.hsc" #-}

instance Storable SockAddrBTH where
    sizeOf _ = (10)
{-# LINE 95 "Network/Bluetooth/Device.hsc" #-}
    alignment _ = alignment (undefined :: Word64)
    peek _ = fail "SockAddrBTH.peek not defined"
    poke p (SockAddrBTH family bdaddr channel) = do
        let p_family = p `plusPtr` (0) :: Ptr ()
{-# LINE 99 "Network/Bluetooth/Device.hsc" #-}
            p_bdaddr = p `plusPtr` (2)
{-# LINE 100 "Network/Bluetooth/Device.hsc" #-}
            p_channel = p `plusPtr` (8)
{-# LINE 101 "Network/Bluetooth/Device.hsc" #-}
        case (2) of
{-# LINE 102 "Network/Bluetooth/Device.hsc" #-}
            1 -> poke (castPtr p_family) (fromIntegral family :: Word8)
            2 -> poke (castPtr p_family) (fromIntegral family :: Word16)
            4 -> poke (castPtr p_family) (fromIntegral family :: Word32)
            sz -> fail $ "SockAddrBTH.poke can't handle size "++show sz
        B.unsafeUseAsCString bdaddr $ \c_bdaddr ->
            B.memcpy p_bdaddr (castPtr c_bdaddr) (B.length bdaddr)
        poke p_channel channel

{-# LINE 110 "Network/Bluetooth/Device.hsc" #-}


{-# LINE 114 "Network/Bluetooth/Device.hsc" #-}
foreign import ccall unsafe "connect"

{-# LINE 116 "Network/Bluetooth/Device.hsc" #-}
  c_connect :: CInt -> Ptr SockAddrBTH -> CInt -> IO CInt


{-# LINE 122 "Network/Bluetooth/Device.hsc" #-}

openRFCOMM :: Device -> Word8 -> IO RFCOMMSocket
openRFCOMM dev@(Device _ addr) channel = do

{-# LINE 130 "Network/Bluetooth/Device.hsc" #-}
    s <- socket AF_BLUETOOTH Stream (3)
{-# LINE 131 "Network/Bluetooth/Device.hsc" #-}
    setSocketOption s ReusePort 1

{-# LINE 133 "Network/Bluetooth/Device.hsc" #-}
    connect s `onException` sClose s
  where
    connect s = do
        let fd = fdSocket s
        ret <- alloca $ \p_sarc -> do
            poke p_sarc (sockAddrBTH addr channel)
            c_connect fd p_sarc (fromIntegral $ sizeOf (undefined :: SockAddrBTH))
        if ret < 0 then do
            errno@(Errno errno_) <- getErrno
            case errno of
                _ | errno == eINTR -> connect s
                _ | errno == eOK -> return $ RFCOMMSocket s
                _ | errno == eINPROGRESS -> do
                    threadWaitWrite (fromIntegral fd)
                    errno@(Errno errno_) <- Errno . fromIntegral <$> getSocketOption s SoError
                    if errno == eOK
                        then return $ RFCOMMSocket s
                        else do

{-# LINE 155 "Network/Bluetooth/Device.hsc" #-}
                            err <- peekCString (strerror errno_)
                            throwIO $ BluetoothException "openRFCOMM" err

{-# LINE 158 "Network/Bluetooth/Device.hsc" #-}
                _ -> do

{-# LINE 163 "Network/Bluetooth/Device.hsc" #-}
                    err <- peekCString (strerror errno_)
                    throwIO $ BluetoothException "openRFCOMM" err

{-# LINE 166 "Network/Bluetooth/Device.hsc" #-}
          else
            return $ RFCOMMSocket s

recvRFCOMM :: RFCOMMSocket -> Int -> IO ByteString
recvRFCOMM (RFCOMMSocket s) n = NB.recv s n
  `catch` \exc ->
     throwIO (BluetoothException "recvRFCOMM" (show (exc :: IOException)))

sendRFCOMM :: RFCOMMSocket -> ByteString -> IO Int
sendRFCOMM (RFCOMMSocket s) txt = NB.send s txt
  `catch` \exc ->
     throwIO (BluetoothException "sendRFCOMM" (show (exc :: IOException)))

sendAllRFCOMM :: RFCOMMSocket -> ByteString -> IO ()
sendAllRFCOMM (RFCOMMSocket s) txt = NB.sendAll s txt
  `catch` \exc ->
     throwIO (BluetoothException "sendAllRFCOMM" (show (exc :: IOException)))

closeRFCOMM :: RFCOMMSocket -> IO ()
closeRFCOMM (RFCOMMSocket s) = sClose s
  `catch` \exc ->
     throwIO (BluetoothException "close" (show (exc :: IOException)))