content: {-| Client functionality for the <http://wiki.ros.org/ROS/Master_API ROS Master API>
  -}
  module Ros.Graph.Master where
  import Network.XmlRpc.Client
  import Ros.Internal.RosTypes
  import Ros.Service.ServiceTypes
  import Network.XmlRpc.Internals (fromValue, toValue)
  import Control.Monad.Except (ExceptT(..), runExceptT)
  import System.IO.Error (catchIOError)
  
  -- |Subscribe the caller to the specified topic. In addition to
  -- receiving a list of current publishers, the subscriber will also
  -- receive notifications of new publishers via the publisherUpdate
  -- API. Takes the URI of the master, the caller_id, the topic name,
  -- the topic type (must be a package-resource name, i.e. the .msg
  -- name), and the API URI of the subscriber to register (used for new
  -- publisher notifications). Returns a list of XML-RPC API URIs for
  -- nodes currently publishing the specified topic.
  registerSubscriber :: URI -> String -> TopicName -> TopicType -> String -> 
                        IO (Int, String, [String])
  registerSubscriber = flip remote "registerSubscriber"
  
  -- |Unregister the caller as a subscriber of the topic. Takes the URI
  -- of the master, the caller_id, the topic name, and the API URI of
  -- the subscriber to unregister. Returns zero if the caller was not
  -- registered as a subscriber.
  unregisterSubscriber :: URI -> String -> TopicName -> String -> 
                          IO (Int, String, Int)
  unregisterSubscriber = flip remote "unregisterSubscriber"
  
  -- |Register the caller as a publisher the topic. Takes the URI of the
  -- master, the caller_id, the topic name, the topic type (must be a
  -- package-resource name, i.e. the .msg name), and the API URI of the
  -- publisher to register. Returns a list of the XML-RPC URIs of
  -- current subscribers of the topic.
  registerPublisher :: URI -> String -> TopicName -> TopicType -> String -> 
                       IO (Int, String, [String])
  registerPublisher = flip remote "registerPublisher"
  
  -- |Unregister the caller as a publisher of the topic. Takes the URI of
  -- the master, caller_id, the topic name, and the API URI of the
  -- publisher to unregister. Returns zero if the caller was not
  -- registered as a publisher.
  unregisterPublisher :: URI -> String -> TopicName -> String -> 
                         IO (Int, String, Int)
  unregisterPublisher = flip remote "unregisterPublisher"
  
  -- |@lookupService u s1 s2@ where @u@ is the URI of the ROS master,
  -- @s1@ is the ROS caller ID, and @s2@ is the ROS fully-qualified name
  -- of the service
  --
  -- In the result tuple @(code, statusMessage, serviceUrl)@, @code@
  -- should be 1 upon a sucessful call. If @code@ is not 1 then
  -- @statusMessage@ may contain an error message. The host name and
  -- port of the service can be extracted from the @serviceUrl@
  --
  lookupService :: URI -> String -> ServiceName 
                -> ExceptT ServiceResponseExcept IO (Int, String, String)
  lookupService u s1 s2 = ExceptT . (flip catchIOError) handler $ do
    let res = call u "lookupService" (fmap toValue [s1, s2]) >>= fromValue
    err <- runExceptT res
    return $ case err of
      Left x -> Left $ MasterExcept $ "Could not look up service with master. Got message: " ++ x
      Right y -> Right y
    where
      handler x =  return . Left . MasterExcept $
                   "Could not look up service with master. Is the ROS master (roscore) running? Got exception: " ++ show x
  
  -- | ROS API: registerService(caller_id, service, service_api, caller_api)
  -- Register the caller as a provider of the specified service.
  -- Parameters
  -- caller_id (str)
  -- ROS caller ID
  -- service (str)
  -- Fully-qualified name of service
  -- service_api (str)
  -- ROSRPC Service URI
  -- caller_api (str)
  -- XML-RPC URI of caller node
  -- Returns (int, str, int)
  -- (code, statusMessage, ignore)
  registerService :: URI -> String -> ServiceName -> URI -> URI -> IO(Int, String, Int)
  registerService = flip remote "registerService"
  
  
  -- | ROS API: unregisterService(caller_id, service, service_api)
  -- Unregister the caller as a provider of the specified service.
  -- Parameters
  -- caller_id (str)
  -- ROS caller ID
  -- service (str)
  -- Fully-qualified name of service
  -- service_api (str)
  -- API URI of service to unregister. Unregistration will only occur if current registration matches.
  -- Returns (int, str, int)
  -- (code, statusMessage, numUnregistered).
  -- Number of unregistrations (either 0 or 1). If this is zero it means that the caller was not registered as a service provider. The call still succeeds as the intended final state is reached.
  unregisterService :: URI -> String -> ServiceName -> URI -> IO(Int, String, Int)
  unregisterService = flip remote "unregisterService"
  