from ..common import *


class TD_COAP_OBS_04(CoAPTestCase):
    """
---
TD_COAP_OBS_04:
    obj: Client detection of deregistration (Max-Age)
    cfg: CoAP_CFG_BASIC
    ref: '[OBSERVE] 3.3.1 §4'

    pre:
        - Client supports Observe option
        - Server supports Observe option
        - "Server offers an observable resource /obs which changes periodically
           (e.g. every 5s) which produces confirmable notifications"
    seq:
    -   s: "Client is requested to send to the server a confirmable GET
            request with observe option for resource /obs"

    -   c:
        - 'The request sent by client contains:'
        -   - Type = 0 (CON)
            - Code = 1 (GET)
            - Token value  = a value generated by the client
            - Observe option = empty

    -   c:
        - 'Server sends the response containing:'
        -   - Type = 2 (ACK)
            - Code = 2.05 (Content)
            - Content-format of the resource /obs
            - Token value = same as one found in the step 2
            - Observe option with a sequence number

    -   c:
        - 'Server sends a notification containing:'
        -   - Type = 0 (CON)
            - Code = 2.05 (Content)
            - Content-format = same as one found in the step 3
            - Token value = same as one found in the step 2
            - Observe option indicating increasing values

    -   v: Client displays the received information

    -   c: Client sends an ACK

    -   s: Server is rebooted or in another way caused to lose its observation state

    -   c:
        - Server does not send notifications

    -   v: Client does not display updated information

    -   v:After a while (see note) the client internally decides to send another GET request to the server with observe option for resource /obs, using Token t again to confirm the registration

    -   v:
    - 'Client sends a GET request to the server for resource /obs:'
    -   - Type = 0(CON)
        - Code = 1(GET)
        - Token value  = same as one found in the step 2
        - Observe option = empty

    -   c:
        - 'Server sends the response containing:'
        -   -Type = 2(ACK)
            - Code = 2.05(Content)
            - Content-format of the resource /obs
            - Token value = same as one found in the step 2
            - Observe option with a sequence number that is not necessarily increasing

    -   c:
        - 'Server sends a notification containing:'
        -   - Type = 0(CON)
            - Code = 2.05(Content)
            - Content-format = same as one found in the step 12
            - Token value = same as one found in the step 2
            - Observe option indicating increasing values
    -   v: Client displays the received information

    -   c: Client sends an ACK

    """
    @classmethod
    @typecheck
    def get_stimulis(cls) -> list_of(Value):
        """
        Get the stimulis of this test case. This has to be be implemented into
        each test cases class.

        :return: The stimulis of this TC
        :rtype: [Value]
        """
        return [
                CoAP(type='con', code='get', opt=Opt(CoAPOptionObserve(0),CoAPOptionUriPath("obs")))
        ]

    def run(self):
                request = CoAP(type="con", code="get",	opt=self.uri("/obs", CoAPOptionObserve(0)))
                response = CoAP(type="ack", code=2.05, opt=Opt(CoAPOptionObserve(), CoAPOptionContentFormat()))

                # Step 2
                self.match("client", request)

                token = self.coap["tok"]
                uri = self.coap.get_uri()

                self.next()

                # Step 3
                if not self.match("server", response):
                    raise self.Stop()
                self.match("server", CoAP(tok = token), "fail")

                content_format = self.coap["opt"][CoAPOptionContentFormat]["val"]
                index          = self.coap["opt"][CoAPOptionObserve]["val"]

                self.next()

                # we need to observe at least one notification
                # (to ensure that the server is observing the resource for the client)

                verdict_if_none = "inconclusive"
                log_message_if_not_pass = "Test case did not encounter any re-registration (STEP 10).\
It may be because the server did not lost it's observation state as expected in step 7,\
or because the client did not try to re-register."
                client_did_re_register = False
                client_received_notification_after_reregistering = False
                # Step 4
                while self.match("server", CoAP(type="con", code=2.05, opt=Opt(CoAPOptionObserve())), verdict_if_none, log_message_if_not_pass):
                    self.match("server", CoAP(tok = token), "fail")
                    self.match("server", CoAP(opt = Opt(CoAPOptionContentFormat(content_format))), "fail")

                    new_index = self.coap["opt"][CoAPOptionObserve]["val"]

                    self.set_verdict(("pass" if new_index > index else "fail"),
                    "value of observe option must be increasing")

                    self.next()

                    # Step 6
                    self.match("client",CoAP(type="ack", code=0), verdict_if_none, log_message_if_not_pass)
                    # We pass client_received_notification_after_reregistering to
                    # as argument as if it's true, it may be the end of the
                    # conversation anytime.
                    # It avoid having a wrong inconclusive verdict
                    # with "premature end of conversation" when it should
                    # be a pass verdict.
                    self.next(client_received_notification_after_reregistering)
                    if client_did_re_register:
                        # Step 13
                        client_received_notification_after_reregistering = True
                        verdict_if_none = None
                    else:
                        client_did_re_register = self.__client_re_registred_with_same_token(token)
                        skiped_all_retry = False
                        while not skiped_all_retry and client_did_re_register:
                            # We do this loop because the client may do several
                            # retries before being successfully registred again.
                            retry = self.__client_re_registred_with_same_token(token)
                            skiped_all_retry = not retry
                        if client_did_re_register:
                            # Step 12
                            self.match("server",
                                        CoAP(type="ack", tok=token, code=2.05, opt=Opt(CoAPOptionObserve(),CoAPOptionContentFormat())), "fail",
                                        "Client did re_register as expected, but the server did not send new notification")
                            index = self.coap["opt"][CoAPOptionObserve]["val"]
                            self.next()

    def __client_re_registred_with_same_token(self, token):
        re_registered = self.match("client",CoAP(type='con', code='get',
        tok=token, opt=Opt(CoAPOptionObserve(0),CoAPOptionUriPath("obs"))), None)
        if re_registered:
            self.next()
        return re_registered
