----------------------------------------------------------------------------
-- |
-- Module      :  WSJTX.UDP.Server
-- Copyright   :  (c) Marc Fontaine 2017-2018
-- License     :  BSD3
-- 
-- Maintainer  :  Marc.Fontaine@gmx.de
-- Stability   :  experimental
-- Portability :  GHC-only
--
-- Receive UDP packages from WSJT-X and reply to UDP packages.
-- The UDP destination port address to reach the WSJT-X application
-- is the the same address that the WSJT-X uses as source address.
-- (That not the address that is configured in the GUI).
-- This address is only known after the first package is received from WSJT-X.
-- (WSJT-X regularly sends heard beat packages.)

module WSJTX.UDP.Server
where

import Control.Monad
import Control.Concurrent

import Control.Exception.Base (bracket)
import Network.Socket hiding (send, recv, recvFrom)
import Network.Socket.ByteString (send, recv , recvFrom)

import WSJTX.UDP.NetworkMessage
import WSJTX.UDP.EncodeQt (packageToUDP, parseUDPPackage)

wsjtxDefaultPort :: PortNumber
wsjtxDefaultPort = 2237

testDump :: IO ()
testDump = withWsjtxSocket wsjtxDefaultPort $ \sock -> do
  _threadId <- forkWsjtxServer sock print
  void getLine

withWsjtxSocket :: PortNumber -> (Socket -> IO a) -> IO a
withWsjtxSocket port
  = bracket (openSocket port) close

forkWsjtxServer :: Socket -> (Package -> IO ()) -> IO ThreadId
forkWsjtxServer conn callback = forkIO $ forever $ do
  msg <- recv conn 1024
  callback $  parseUDPPackage msg

openSocket :: PortNumber -> IO Socket
openSocket udpPort = do
  sock <- socket AF_INET Datagram defaultProtocol
  bind sock $ SockAddrInet udpPort (tupleToHostAddress (127,0,0,1))
  return sock

replyWithPackages :: PortNumber -> [Package] -> IO ()
replyWithPackages udpPort packages = bracket
  (do
     sock <- socket AF_INET Datagram defaultProtocol
     bind sock $ SockAddrInet udpPort (tupleToHostAddress (127,0,0,1))
-- wait for a package from the client to find out the address
     (_,addr) <- recvFrom sock 1024
     connect sock addr
     return sock
   )
   close
   (\sock -> forM_ packages (send sock . packageToUDP))