'''This module is intended for any wrappers that are needed for rospy'''
import time
import rospy
import logging
from markov.log_handler.logger import Logger
from markov.log_handler.exception_handler import log_and_exit
from markov.log_handler.constants import (SIMAPP_SIMULATION_WORKER_EXCEPTION,
                                          SIMAPP_EVENT_ERROR_CODE_500)
from markov.constants import ROBOMAKER_CANCEL_JOB_WAIT_TIME


ROS_SERVICE_ERROR_MSG_FORMAT = "ROS Service {0} call failed, Re-try count: {1}/{2}: {3}"

logger = Logger(__name__, logging.INFO).get_logger()


class ServiceProxyWrapper(object):
    """This class wraps rospy's ServiceProxy method so that we can wait
       5 minutes if a service throws an exception. This is required to prevent
       our metrics from being flooded since an exception is thrown by service
       calls when the cancel simulation API is called. Because robomaker gives
       us no way of knowing whether or not the exception is real or because the
       sim app is shutting down we have to wait 5 minutes prior logging the exception
       and exiting.
    """
    def __init__(self, service_name, object_type, persistent=False,
                 max_retry_attempts=5):
        """service_name (str): Name of the service to create a client for
           object_type (object): The object type for making a service request
           persistent (bool): flag to whether keep the connection open or not
           max_retry_attempts (int): maximum number of retry
        """
        self.client = rospy.ServiceProxy(service_name, object_type, persistent)
        self._service_name = service_name
        self._max_retry_attempts = max_retry_attempts

    def __call__(self, *argv):
        """ Makes a client call for the stored service
            argv (list): Arguments to pass into the client object
        """
        try_count = 0
        while True:
            try:
                return self.client(*argv)
            except TypeError as err:
                log_and_exit("Invalid arguments for client {}".format(err),
                             SIMAPP_SIMULATION_WORKER_EXCEPTION,
                             SIMAPP_EVENT_ERROR_CODE_500)
            except Exception as ex:
                try_count += 1
                if try_count > self._max_retry_attempts:
                    time.sleep(ROBOMAKER_CANCEL_JOB_WAIT_TIME)
                    log_and_exit("Unable to call service {}".format(ex),
                                 SIMAPP_SIMULATION_WORKER_EXCEPTION,
                                 SIMAPP_EVENT_ERROR_CODE_500)
                error_message = ROS_SERVICE_ERROR_MSG_FORMAT.format(self._service_name,
                                                                    str(try_count),
                                                                    str(self._max_retry_attempts),
                                                                    ex)
                logger.info(error_message)