# -*- coding: utf-8 -*-
"""
ldap.controls.sss - classes for Server Side Sorting
(see RFC 2891)
See http://www.python-ldap.org/ for project details.
$Id: sss.py,v 1.2 2015/10/24 15:52:23 stroeder Exp $
"""

__all__ = [
    'SSSRequestControl',
    'SSSResponseControl',
]

from ldap.controls import (RequestControl, ResponseControl,
                           KNOWN_RESPONSE_CONTROLS)
from pyasn1.codec.ber import encoder, decoder
from pyasn1.type import univ, namedtype, tag, namedval, constraint


#    SortKeyList ::= SEQUENCE OF SEQUENCE {
#                     attributeType   AttributeDescription,
#                     orderingRule    [0] MatchingRuleId OPTIONAL,
#                     reverseOrder    [1] BOOLEAN DEFAULT FALSE }


class SortKeyType(univ.Sequence):
    componentType = namedtype.NamedTypes(
        namedtype.NamedType('attributeType', univ.OctetString()),
        namedtype.OptionalNamedType('orderingRule',
                                    univ.OctetString().subtype(
                                        implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)
                                    )
                                    ),
        namedtype.DefaultedNamedType('reverseOrder', univ.Boolean(False).subtype(
            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))))


class SortKeyListType(univ.SequenceOf):
    componentType = SortKeyType()


class SSSRequestControl(RequestControl):
    '''Order result server side
        >>> s = SSSRequestControl('-cn')
    '''
    controlType = '1.2.840.113556.1.4.473'

    def __init__(
            self,
            criticality=False,
            ordering_rules=None,
    ):
        RequestControl.__init__(self, self.controlType, criticality)
        self.ordering_rules = ordering_rules
        if isinstance(ordering_rules, basestring):
            ordering_rules = [ordering_rules]
        for rule in ordering_rules:
            rule = rule.split(':')
            assert len(rule) < 3, 'syntax for ordering rule: [-]<attribute-type>[:ordering-rule]'

    def asn1(self):
        p = SortKeyListType()
        for i, rule in enumerate(self.ordering_rules):
            q = SortKeyType()
            reverse_order = rule.startswith('-')
            if reverse_order:
                rule = rule[1:]
            if ':' in rule:
                attribute_type, ordering_rule = rule.split(':')
            else:
                attribute_type, ordering_rule = rule, None
            q.setComponentByName('attributeType', attribute_type)
            if ordering_rule:
                q.setComponentByName('orderingRule', ordering_rule)
            if reverse_order:
                q.setComponentByName('reverseOrder', 1)
            p.setComponentByPosition(i, q)
        return p

    def encodeControlValue(self):
        return encoder.encode(self.asn1())


class SortResultType(univ.Sequence):
    componentType = namedtype.NamedTypes(
        namedtype.NamedType('sortResult', univ.Enumerated().subtype(
            namedValues=namedval.NamedValues(
                ('success', 0),
                ('operationsError', 1),
                ('timeLimitExceeded', 3),
                ('strongAuthRequired', 8),
                ('adminLimitExceeded', 11),
                ('noSuchAttribute', 16),
                ('inappropriateMatching', 18),
                ('insufficientAccessRights', 50),
                ('busy', 51),
                ('unwillingToPerform', 53),
                ('other', 80)),
            subtypeSpec=univ.Enumerated.subtypeSpec + constraint.SingleValueConstraint(
                0, 1, 3, 8, 11, 16, 18, 50, 51, 53, 80))),
        namedtype.OptionalNamedType('attributeType',
                                    univ.OctetString().subtype(
                                        implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)
                                    )
                                    ))


class SSSResponseControl(ResponseControl):
    controlType = '1.2.840.113556.1.4.474'

    def __init__(self, criticality=False):
        ResponseControl.__init__(self, self.controlType, criticality)

    def decodeControlValue(self, encoded):
        p, rest = decoder.decode(encoded, asn1Spec=SortResultType())
        assert not rest, 'all data could not be decoded'
        self.result = int(p.getComponentByName('sortResult'))
        self.result_code = p.getComponentByName('sortResult').prettyOut(self.result)
        self.attribute_type_error = p.getComponentByName('attributeType')


KNOWN_RESPONSE_CONTROLS[SSSRequestControl.controlType] = SSSRequestControl
KNOWN_RESPONSE_CONTROLS[SSSResponseControl.controlType] = SSSResponseControl
