module System.Hardware.Serialport.Posix where

import System.IO
import System.Posix.IO
import System.Posix.Terminal
import System.Hardware.Serialport.Types


-- |Open and configure a serial port
openSerial :: FilePath     -- ^ The filename of the serial port, such as @\/dev\/ttyS0@ or @\/dev\/ttyUSB0@
           -> SerialPortSettings
           -> IO SerialPort
openSerial dev settings =
  do setSerialSettings dev settings
     h <- openBinaryFile dev ReadWriteMode
     hSetBuffering h NoBuffering
     return $ SerialPort h settings
        

-- |Possibly receive a character unless the timeout given in openSerial is exceeded.
recvChar :: SerialPort -> IO (Maybe Char)
recvChar (SerialPort h settings) =
  do have_input <- hWaitForInput h ((timeout settings) * 100)
     if have_input
        then do c <- hGetChar h
                return $ Just c
        else return Nothing


-- |Send a character
sendChar :: SerialPort -> Char -> IO ()
sendChar (SerialPort h _ ) c =
    hPutChar h c >> return ()


-- |Close the serial port
closeSerial :: SerialPort -> IO ()
closeSerial (SerialPort h _ ) =
    hClose h


setSerialSettings :: FilePath -> SerialPortSettings -> IO ()
setSerialSettings dev settings = 
  do fd <- openFd dev ReadWrite Nothing 
           OpenFileFlags { append = True,
                           exclusive = True,
                           noctty = True,
                           nonBlock = False,
                           trunc = False }
     termOpts <- getTerminalAttributes fd
     let termOpts' = configureSettings termOpts settings
     setTerminalAttributes fd termOpts' Immediately
     closeFd fd


withParity :: TerminalAttributes -> Parity -> TerminalAttributes
withParity termOpts Even = termOpts `withMode` EnableParity 
                                    `withoutMode` OddParity
withParity termOpts Odd = termOpts `withMode` EnableParity
                                   `withMode` OddParity
withParity termOpts NoParity = termOpts `withoutMode` EnableParity


withFlowControl :: TerminalAttributes -> FlowControl -> TerminalAttributes
withFlowControl termOpts NoFlowControl = termOpts
                                         `withoutMode` StartStopInput
                                         `withoutMode` StartStopOutput
withFlowControl termOpts Software = termOpts
                                    `withMode` StartStopInput
                                    `withMode` StartStopOutput


withStopBits :: TerminalAttributes -> StopBits -> TerminalAttributes
withStopBits termOpts One = termOpts `withoutMode` TwoStopBits
withStopBits termOpts Two = termOpts `withMode` TwoStopBits


configureSettings :: TerminalAttributes -> SerialPortSettings -> TerminalAttributes
configureSettings termOpts settings =
    termOpts `withInputSpeed` (baudRate settings)
                 `withOutputSpeed` (baudRate settings)
                 `withBits` (fromIntegral (bitsPerWord settings))
                 `withStopBits` (stopb settings)
                 `withParity` (parity settings)
                 `withFlowControl` (flowControl settings)
                 `withoutMode` EnableEcho
                 `withoutMode` EchoErase
                 `withoutMode` EchoKill
                 `withoutMode` ProcessInput
                 `withoutMode` ProcessOutput
                 `withoutMode` MapCRtoLF
                 `withoutMode` EchoLF
                 `withoutMode` HangupOnClose
                 `withoutMode` KeyboardInterrupts
                 `withoutMode` ExtendedFunctions
                 `withMode` LocalMode
                 `withMode` ReadEnable