class CommunicationTokenCredential(object):
    """Credential type used for authenticating to an Azure Communication service.
    :param str token: The token used to authenticate to an Azure Communication service
    :keyword token_refresher: The token refresher to provide capacity to fetch fresh token
    :raises: TypeError
    """

    _ON_DEMAND_REFRESHING_INTERVAL_MINUTES = 2

    def __init__(self,
                 token,  # type: str
                 **kwargs
                 ):
        token_refresher = kwargs.pop('token_refresher', None)
        communication_token_refresh_options = CommunicationTokenRefreshOptions(token=token,
                                                                               token_refresher=token_refresher)
        self._token = communication_token_refresh_options.get_token()
        self._token_refresher = communication_token_refresh_options.get_token_refresher()
        self._lock = Condition(Lock())
        self._some_thread_refreshing = False

    def get_token(self):
        # type () -> ~azure.core.credentials.AccessToken
        """The value of the configured token.
        :rtype: ~azure.core.credentials.AccessToken
        """

        if not self._token_refresher or not self._token_expiring():
            return self._token

        should_this_thread_refresh = False

        with self._lock:

            while self._token_expiring():
                if self._some_thread_refreshing:
                    if self._is_currenttoken_valid():
                        return self._token

                    self._wait_till_inprogress_thread_finish_refreshing()
                else:
                    should_this_thread_refresh = True
                    self._some_thread_refreshing = True
                    break


        if should_this_thread_refresh:
            try:
                newtoken = self._token_refresher()  # pylint:disable=not-callable

                with self._lock:
                    self._token = newtoken
                    self._some_thread_refreshing = False
                    self._lock.notify_all()
            except:
                with self._lock:
                    self._some_thread_refreshing = False
                    self._lock.notify_all()

                raise

        return self._token

    def _wait_till_inprogress_thread_finish_refreshing(self):
        self._lock.release()
        self._lock.acquire()

    def _token_expiring(self):
        return self._token.expires_on - self._get_utc_now_as_int() <\
               timedelta(minutes=self._ON_DEMAND_REFRESHING_INTERVAL_MINUTES).total_seconds()

    def _is_currenttoken_valid(self):
        return self._get_utc_now_as_int() < self._token.expires_on

    @classmethod
    def _get_utc_now_as_int(cls):
        current_utc_datetime = datetime.utcnow().replace(tzinfo=TZ_UTC)
        current_utc_datetime_as_int = _convert_datetime_to_utc_int(current_utc_datetime)
        return current_utc_datetime_as_int
class CommunicationTokenCredential(object):
    """Credential type used for authenticating to an Azure Communication service.
    :param str token: The token used to authenticate to an Azure Communication service
    :raises: TypeError
    """

    ON_DEMAND_REFRESHING_INTERVAL_MINUTES = 2

    def __init__(
            self,
            token,  # type: str
            token_refresher=None):
        # type: (str) -> None
        if not isinstance(token, six.string_types):
            raise TypeError("token must be a string.")
        self._token = create_access_token(token)
        self._token_refresher = token_refresher
        self._lock = Condition(Lock())
        self._some_thread_refreshing = False

    def get_token(self):
        # type () -> ~azure.core.credentials.AccessToken
        """The value of the configured token.
        :rtype: ~azure.core.credentials.AccessToken
        """

        if not self._token_refresher or not self._token_expiring():
            return self._token

        should_this_thread_refresh = False

        with self._lock:

            while self._token_expiring():
                if self._some_thread_refreshing:
                    if self._is_currenttoken_valid():
                        return self._token

                    self._wait_till_inprogress_thread_finish_refreshing()
                else:
                    should_this_thread_refresh = True
                    self._some_thread_refreshing = True
                    break

        if should_this_thread_refresh:
            try:
                newtoken = self._token_refresher()

                with self._lock:
                    self._token = newtoken
                    self._some_thread_refreshing = False
                    self._lock.notify_all()
            except:
                with self._lock:
                    self._some_thread_refreshing = False
                    self._lock.notify_all()

                raise

        return self._token

    def _wait_till_inprogress_thread_finish_refreshing(self):
        self._lock.release()
        self._lock.acquire()

    def _token_expiring(self):
        return self._token.expires_on - self._get_utc_now() <\
            timedelta(minutes=self.ON_DEMAND_REFRESHING_INTERVAL_MINUTES)

    def _is_currenttoken_valid(self):
        return self._get_utc_now() < self._token.expires_on

    @classmethod
    def _get_utc_now(cls):
        return datetime.now().replace(tzinfo=TZ_UTC)