from . import *
from udsoncan.Response import Response
from udsoncan.exceptions import *

class SecurityAccess(BaseService):
    _sid = 0x27

    supported_negative_response = [	Response.Code.SubFunctionNotSupported, 
                                                    Response.Code.IncorrectMessageLegthOrInvalidFormat,
                                                    Response.Code.ConditionsNotCorrect,
                                                    Response.Code.RequestSequenceError,
                                                    Response.Code.RequestOutOfRange,
                                                    Response.Code.InvalidKey,
                                                    Response.Code.ExceedNumberOfAttempts,
                                                    Response.Code.RequiredTimeDelayNotExpired
                                                    ]

    class Mode:
        RequestSeed=0
        SendKey=1

    @classmethod 
    def normalize_level(cls, mode, level):
        cls.validate_mode(mode)
        ServiceHelper.validate_int(level, min=0, max=0x7F, name='Security level')

        if mode == cls.Mode.RequestSeed:
            return level if level % 2 == 1 else level-1
        elif mode == cls.Mode.SendKey:
            return level if level % 2 == 0 else level+1

    @classmethod
    def make_request(cls, level, mode, key=None):
        """
        Generates a request for SecurityAccess

        :param level: Service subfunction. The security level to unlock. Value ranging from 0 to 7F 
                For mode=``RequestSeed`` (0), level must be an even value. For mode=``SendKey`` (1), level must be an odd value.
                If the even/odd constraint is not respected, the level value will be corrected to properly set the LSB.
        :type level: int

        :param mode: Type of request to perform. ``SecurityAccess.Mode.RequestSeed`` or ``SecurityAccess.Mode.SendKey`` 
        :type mode: SecurityAccess.Mode, int

        :param key: When mode=``SendKey``, this value must be provided.
        :type key: bytes

        :raises ValueError: If parameters are out of range, missing or wrong type
        """		
        from udsoncan import Request
        cls.validate_mode(mode)

        ServiceHelper.validate_int(level, min=0, max=0x7F, name='Security level')
        req = Request(service=cls, subfunction=cls.normalize_level(mode=mode, level=level))

        if mode == cls.Mode.SendKey:
            if not isinstance(key, bytes):
                raise ValueError('key must be a valid bytes object')
            req.data = key

        return req

    @classmethod
    def interpret_response(cls, response, mode):
        """
        Populates the response ``service_data`` property with an instance of :class:`SecurityAccess.ResponseData<udsoncan.services.SecurityAccess.ResponseData>`

        :param response: The received response to interpret
        :type response: :ref:`Response<Response>`

        :raises InvalidResponseException: If length of ``response.data`` is too short
        :raises ValueError: If mode is not ``RequestSeed`` or ``SendKey``
        """

        cls.validate_mode(mode)

        response.service_data = cls.ResponseData()
        minlength = 2 if mode == cls.Mode.RequestSeed else 1

        if len(response.data) < minlength:
            raise InvalidResponseException(response, "Response data must be at least %d bytes" % (minlength))

        response.service_data.security_level_echo = response.data[0]

        if mode == cls.Mode.RequestSeed:
            response.service_data.seed = response.data[1:]

    @classmethod
    def validate_mode(cls, mode):
        if mode not in [cls.Mode.RequestSeed, cls.Mode.SendKey]:
            raise ValueError('Given mode must be either be RequestSeed (0) or SendKey (1).')

    class ResponseData(BaseResponseData):
        """
        .. data:: security_level_echo

                Requests subfunction echoed back by the server

        .. data:: seed

                Seed value. Only present if request mode was ``RequestSeed`` (even subfunction)
        """		
        def __init__(self):
            super().__init__(SecurityAccess)

            self.security_level_echo = None
            self.seed = None
