import logging

import ldap
from ldap.controls import SimplePagedResultsControl


logger = logging.getLogger(__name__)


class LDAPSearch(object):
    _ldap = None

    def __init__(self, settings):
        self.settings = settings

    def __del__(self):
        self._unbind()

    @property
    def ldap(self):
        if self._ldap is None:
            self._ldap = self._bind()
        return self._ldap

    def _bind(self):
        ldap.set_option(ldap.OPT_REFERRALS, 0)
        l = ldap.initialize(self.settings.URI)
        l.protocol_version = ldap.VERSION3
        try:
            l.simple_bind_s(self.settings.BASE_USER, self.settings.BASE_PASS)
        except ldap.LDAPError as e:
            logger.error("Error connecting to %s: %s" % (self.settings.URI, e))
            raise
        return l

    def _unbind(self):
        if self._ldap is not None:
            self.ldap.unbind_s()
            self._ldap = None

    def search(self, filterstr, attrlist):
        """Query the configured LDAP server."""
        return self._paged_search_ext_s(self.settings.BASE, ldap.SCOPE_SUBTREE, filterstr=filterstr,
                                        attrlist=attrlist, page_size=self.settings.PAGE_SIZE)

    def _paged_search_ext_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0,
                            serverctrls=None, clientctrls=None, timeout=-1, sizelimit=0, page_size=10):
        """
        Behaves similarly to LDAPObject.search_ext_s() but internally uses the
        simple paged results control to retrieve search results in chunks.

        Taken from the python-ldap paged_search_ext_s.py demo, showing how to use
        the paged results control: https://bitbucket.org/jaraco/python-ldap/
        """
        req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')

        # Send first search request
        msgid = self.ldap.search_ext(base, scope, filterstr=filterstr, attrlist=attrlist, attrsonly=attrsonly,
                                     serverctrls=(serverctrls or []) + [req_ctrl], clientctrls=clientctrls,
                                     timeout=timeout, sizelimit=sizelimit)
        results = []

        while True:
            rtype, rdata, rmsgid, rctrls = self.ldap.result3(msgid)
            results.extend(rdata)
            # Extract the simple paged results response control
            pctrls = [c for c in rctrls if c.controlType == SimplePagedResultsControl.controlType]

            if pctrls:
                if pctrls[0].cookie:
                    # Copy cookie from response control to request control
                    req_ctrl.cookie = pctrls[0].cookie
                    msgid = self.ldap.search_ext(base, scope, filterstr=filterstr, attrlist=attrlist,
                                                 attrsonly=attrsonly, serverctrls=(serverctrls or []) + [req_ctrl],
                                                 clientctrls=clientctrls, timeout=timeout, sizelimit=sizelimit)
                else:
                    break

        return results

@@ -1766,24 +1766,28 @@
 %0A        req
+uest
 _ctrl = Simp
@@ -2060,24 +2060,28 @@
 r %5B%5D) + %5Breq
+uest
 _ctrl%5D, clie
@@ -2235,30 +2235,48 @@
    r
+esult_
 type, r
+esult_
 data, r
+esult_
 msgid, r
 ctrl
@@ -2271,16 +2271,22 @@
 msgid, r
+esult_
 ctrls = 
@@ -2342,14 +2342,21 @@
 nd(r
+esult_
 data)%0A
+%0A
     
@@ -2428,16 +2428,21 @@
        p
+aged_
 ctrls = 
@@ -2454,16 +2454,22 @@
 r c in r
+esult_
 ctrls if
@@ -2546,35 +2546,29 @@
 if p
-ctrls:%0A                if p
+aged_ctrls and paged_
 ctrl
@@ -2580,20 +2580,16 @@
 cookie:%0A
-    
         
@@ -2667,23 +2667,23 @@
         
-    req
+request
 _ctrl.co
@@ -2690,16 +2690,21 @@
 okie = p
+aged_
 ctrls%5B0%5D
@@ -2711,20 +2711,16 @@
 .cookie%0A
-    
         
@@ -2850,28 +2850,24 @@
             
-    
 attrsonly=at
@@ -2917,16 +2917,20 @@
 %5Breq
+uest
 _ctrl%5D,%0A
     
@@ -2925,20 +2925,16 @@
 _ctrl%5D,%0A
-    
         
@@ -3049,22 +3049,14 @@
     
-    
 else:%0A
-    
     

Refactor _paged_search_ext_s() function