from ..common import *

class TD_COAP_OBS_08(CoAPTestCase):
    """
---
TD_COAP_OBS_08:
    obj: Server cleans the observers list when observed resource content-format changes
    cfg: CoAP_CFG_BASIC
    ref: '[OBSERVE] 4.2.3'
    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

    -   c: Client displays the received information

    -   c: Client sends an ACK

    -   s: "Update the /obs resource of the server\u2019s resource
        with a new payload having a different Content-Format (either locally
        or by having another CoAP client perform a \\"DELETE\\" request)"
    -   c:
        - 'Server sends notification containing:'
        -   - Type = 0 (CON)
            - Code = 4.06 (Not Acceptable)
            - Token value = same as one found in the step 2
            - No Observe option any more

    -   v: Server does not send further notifications

    -   v: Client does not display further received information

    """

    @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='post', opt=Opt(CoAPOptionUriPath("obs"), CoAPOptionContentFormat()))
        ]

    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"

        # Optional as the 2nd stimulis may be done internally.
        stimulis = False

        # Step 4
        while not stimulis and self.match("server", CoAP(type="con", code=2.05, opt=Opt(CoAPOptionObserve())),verdict_if_none):
            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")

            new_content_format = self.coap["opt"][CoAPOptionContentFormat]["val"]
            if new_content_format != content_format:
                self.set_verdict(("fail"),
                    "The Content-Format changed but the server did not send\
                    an error as expected")

            self.next()

            stimulis = self.__check_stimulis(content_format)
            if not stimulis:
                # Step 6
                self.match("client", CoAP(type="ack", code=0),"fail")
                # now we have successfully observed a observe response
                self.next()
                verdict_if_none = None

            stimulis = self.__check_stimulis(content_format)

        if stimulis:
            verdict_if_none = "fail"
        else:
            verdict_if_none = None

        if self.match("server",CoAP (type="con",
                                     code=Any(134,160),
                                     opt = NoOpt(CoAPOptionObserve())
                                     ), verdict_if_none):
            self.match("server", CoAP(tok=token), "fail")
            self.next()

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

    def __check_stimulis(self, content_format:str)->bool:
        # Step 7: Stimulus. This step is optional as changing the Content-Format
        # may be done internally by the server.
        # return a boolean that determine if we encountered this optional stimulis

        stimulis = False
        # Optional as stimulis may be done internally.
        CMID_stimulis = None
        CTOK_stimulis = None
        if self.match("client", CoAP(type='con', code='post',
        opt=Opt(CoAPOptionUriPath("obs"), CoAPOptionContentFormat())), None):
            new_content_format = self.coap["opt"][CoAPOptionContentFormat]["val"]
            CMID_stimulis = self.coap["mid"]
            CTOK_stimulis = self.coap["tok"]
            self.set_verdict(("pass" if new_content_format != content_format
                else None),"Stimulus: POST performed with new Content-Format.")
            # stimulis = True
            self.next()

        # We assume that the confirmation come immediatly in the current frame as
        # the preprocess did some reoordering.
        if self.match("server", CoAP(type="ack", code=2.04, tok=CTOK_stimulis,
        mid=CMID_stimulis), None):
            self.set_verdict("pass", "Stimulus: Confirmation from server that \
POST suceeded with new Content-Format")
            self.next()
            stimulis = True

        return stimulis
