from ..common import *


class TD_COAP_OBS_10(CoAPTestCase):
    """
---
TD_COAP_OBS_10:
    obj: GET does not cancel resource observation
    cfg: CoAP_CFG_BASIC
    ref: '[OBSERVE] 1.2, 3, 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:
        -   c: "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

        -   c: Client sends an ACK

        -   v: Client displays the received information

        -   s: "Client is requested to send to the server a confirmable
                GET request *without* observe option for resource /obs"

        -   c:
        - "Client sends a request containing :"
        -  - Type = 0 (CON)
            - Code = 1 (GET)
            - Token value ➔ t2, a value generated by the client ≠ t
            - "*No* Observe option"

        -   c:
        -   - Type = 2 (ACK)
            - Code = 2.05 (Content)
            - Content-format of the resource /obs, = f
            - Token value = t2, same as one found in the step 8
            - "*No* Observe option"

        -   c:
        -   - Type = 0 (CON)
            - Code = 2.05 (Content)
            - Content-format = f, same as one found in the step 3
            - Token value = t, same as one found in the step 3
            - Observe option indicating increasing values

        -   c: Client sends an ACK

        -   v: Client displays the received information

    """
    # "OBS 10 : Consolidated test case and added required stimulis"
    @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"))),
                CoAP(type='con', code='get', opt=All(Opt(CoAPOptionUriPath("obs")),
                                                    NoOpt(CoAPOptionObserve())))
        ]

    def run(self):
        # Step 2
        self.match("client", CoAP(type="con", code="get",
                    opt=self.uri("/obs", CoAPOptionObserve(0))))

        token = self.coap["tok"]
        uri = self.coap.get_uri()
        path = self.coap["opt"][CoAPOptionUriPath]["val"]

        self.next()

        # Step 3
        if not self.match("server", CoAP(type="ack", code=2.05, opt=Opt(
            CoAPOptionObserve(),
            CoAPOptionContentFormat(),
        ))):
            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)

        lastFrame = None

        verdict_if_none = "inconclusive"
        while self.match("server", CoAP(code=2.05, opt=Opt(CoAPOptionObserve())),
                        verdict_if_none):
            # Step 4
            if not self.match("server", CoAP(type="con", code=2.05, opt=Opt(
                CoAPOptionObserve(),
                CoAPOptionContentFormat(),
            ))):
                raise self.Stop()

            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(True)

            # Step 5
            self.match("client", CoAP(type="ack", code=0))

            self.next(True)
            # now we have successfully observed a observe response
            verdict_if_none = None
            if lastFrame == self._frame:
                self.set_verdict("inconclusive",
                    "The stimulus 'Perform an unrelated GET' did not happened\
 or is not visible in any frame this test case operate on")
                raise self.Stop()
            lastFrame = self._frame

        # Step 8
        if self.match("client", CoAP(type="con", code="get"
                                , opt=All(Opt(CoAPOptionUriPath(path)),
                                NoOpt(CoAPOptionObserve()))
                                ) ,None):
            self.set_verdict("pass", "Client performed an unrelated GET.")
            self.match("client", CoAP (tok = Not(token)),"fail")
            token2 = self.coap["tok"]

            self.next()
            # Step 9
            self.match("server", CoAP(type="ack", code=2.05, opt=Opt(
                    CoAPOptionContentFormat(content_format),
                )),"fail")
            self.match("server", CoAP(tok = token2, opt=NoOpt(CoAPOptionObserve())), "fail")

        # Step 10

        while self.match("server", CoAP (type="con",code=2.05, opt=Opt(CoAPOptionObserve(),CoAPOptionContentFormat())), None):
                self.next()
                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 11
                if self.match("client", CoAP(type="rst", code=0), None):
                    rst_sent_by_client = True
                    self.set_verdict("pass", "End of communication from the client.")
                    raise self.Stop()
                elif self.match("client", CoAP(type="ack", code=0, tok = token), None):
                    self.set_verdict("pass", "ACK from client.")
                    self.next()

        raise self.Stop()
