'JSON search method for S3LocationAutocompleteWidget
- adds hierarchy support
@param r: the S3Request
@param attr: request attributes'

@staticmethod
def gis_search_ac(r, **attr):

    output = None
    response = current.response
    resource = r.resource
    table = r.resource.table
    settings = current.deployment_settings
    resource.add_filter(response.s3.filter)
    resource.add_filter((table.end_date == None))
    _vars = current.request.get_vars
    limit = int((_vars.limit or 0))
    value = (_vars.term or _vars.value or _vars.q or None)
    if (not value):
        raise
    value = s3_unicode(value).lower().strip()
    search_l10n = None
    translate = None
    name_alt = settings.get_L10n_name_alt_gis_location()
    levels = _vars.get('levels', None)
    loc_select = _vars.get('loc_select', None)
    if loc_select:
        fields = ['id', 'name']
    else:
        fields = ['id', 'name', 'level', 'L1', 'L2', 'L3', 'L4', 'L5']
        multi_country = (len(current.deployment_settings.get_gis_countries()) != 1)
        if multi_country:
            fields.append('L0')
        if settings.get_L10n_translate_gis_location():
            search_l10n = True
            language = current.session.s3.language
            if (language != current.deployment_settings.get_L10n_default_language()):
                translate = True
                fields.append('path')
    children = _vars.get('children', None)
    if children:
        if (children == 'null'):
            children = None
        else:
            children = int(children)
    level = _vars.get('level', None)
    if level:
        if (level == 'null'):
            level = None
        elif ('|' in level):
            level = level.split('|')
        else:
            level = str.upper(level)
    if children:
        children = current.gis.get_children(children, level=level)
        children = children.find((lambda row: (row.name and (value in str.lower(row.name)))))
        output = children.json()
        response.headers['Content-Type'] = 'application/json'
        return output
    query = FS('name').lower().like((value + '%'))
    field2 = _vars.get('field2', None)
    if field2:
        fieldname = str.lower(field2)
        fields.append(fieldname)
        query |= FS(fieldname).lower().like((value + '%'))
    elif loc_select:
        fields.append('level')
        fields.append('parent')
    else:
        if search_l10n:
            query |= FS('name.name_l10n').lower().like((value + '%'))
            fields.append('name.name_l10n')
        if name_alt:
            query |= FS('name_alt.name_alt').lower().like((value + '%'))
            fields.append('name_alt.name_alt')
    resource.add_filter(query)
    if level:
        if isinstance(level, list):
            query = table.level.belongs(level)
        elif (level == 'NULLNONE'):
            query = (table.level == None)
        elif (level == 'NOTNONE'):
            query = (table.level != None)
        else:
            query = (table.level == level)
    else:
        query = (table.level != 'XX')
    resource.add_filter(query)
    parent = _vars.get('parent', None)
    if parent:
        query = (table.parent == int(parent))
        resource.add_filter(query)
    MAX_SEARCH_RESULTS = current.deployment_settings.get_search_max_results()
    if (((not limit) or (limit > MAX_SEARCH_RESULTS)) and (resource.count() > MAX_SEARCH_RESULTS)):
        output = json.dumps([dict(label=str((current.T('There    are    more    than    %(max)s    results,    please    input    more    characters.') % dict(max=MAX_SEARCH_RESULTS))))], separators=SEPARATORS)
    elif loc_select:
        from s3.s3export import S3Exporter
        output = S3Exporter().json(resource, start=0, limit=limit, fields=fields, orderby=table.name)
    else:
        rows = resource.select(fields=fields, start=0, limit=limit, orderby='gis_location.name')['rows']
        if translate:
            s3db = current.s3db
            l10n_table = s3db.gis_location_name
            l10n_query = ((l10n_table.deleted == False) & (l10n_table.language == language))
            ids = []
            for row in rows:
                path = row['gis_location.path']
                if (not path):
                    path = current.gis.update_location_tree(row['gis_location'])
                ids += path.split('/')
            ids = set(ids)
            l10n_query &= l10n_table.location_id.belongs(ids)
            limitby = (0, len(ids))
            l10n = current.db(l10n_query).select(l10n_table.location_id, l10n_table.name_l10n, limitby=limitby).as_dict(key='location_id')
        items = []
        iappend = items.append
        for row in rows:
            item = {'id': row['gis_location.id']}
            level = row.get('gis_location.level', None)
            if level:
                item['level'] = level
            if translate:
                path = row['gis_location.path']
                ids = path.split('/')
                loc = l10n.get(int(ids.pop()), None)
                if loc:
                    item['name'] = loc['name_l10n']
                else:
                    item['name'] = row['gis_location.name']
            else:
                item['name'] = row['gis_location.name']
            L5 = row.get('gis_location.L5', None)
            if (L5 and (level != 'L5')):
                if translate:
                    loc = l10n.get(int(ids.pop()), None)
                    if loc:
                        item['L5'] = loc['name_l10n']
                    else:
                        item['L5'] = L5
                else:
                    item['L5'] = L5
            L4 = row.get('gis_location.L4', None)
            if (L4 and (level != 'L4')):
                if translate:
                    loc = l10n.get(int(ids.pop()), None)
                    if loc:
                        item['L4'] = loc['name_l10n']
                    else:
                        item['L4'] = L4
                else:
                    item['L4'] = L4
            L3 = row.get('gis_location.L3', None)
            if (L3 and (level != 'L3')):
                if translate:
                    loc = l10n.get(int(ids.pop()), None)
                    if loc:
                        item['L3'] = loc['name_l10n']
                    else:
                        item['L3'] = L3
                else:
                    item['L3'] = L3
            L2 = row.get('gis_location.L2', None)
            if (L2 and (level != 'L2')):
                if translate:
                    loc = l10n.get(int(ids.pop()), None)
                    if loc:
                        item['L2'] = loc['name_l10n']
                    else:
                        item['L2'] = L2
                else:
                    item['L2'] = L2
            L1 = row.get('gis_location.L1', None)
            if (L1 and (level != 'L1')):
                if translate:
                    loc = l10n.get(int(ids.pop()), None)
                    if loc:
                        item['L1'] = loc['name_l10n']
                    else:
                        item['L1'] = L1
                else:
                    item['L1'] = L1
            L0 = row.get('gis_location.L0', None)
            if (L0 and (level != 'L0')):
                if translate:
                    loc = l10n.get(int(ids.pop()), None)
                    if loc:
                        item['L0'] = loc['name_l10n']
                else:
                    item['L0'] = L0
            _name_alt = row.get('gis_location_name_alt.name_alt', None)
            _name_l10n = row.get('gis_location_name.name_l10n', None)
            if isinstance(_name_alt, basestring):
                _name_alt = [_name_alt]
            if isinstance(_name_l10n, basestring):
                _name_l10n = [_name_l10n]
            alternate = dict(item)
            location_names = []
            l = len(value)
            if _name_alt:
                location_names += _name_alt
            if _name_l10n:
                location_names += _name_l10n
            alternate.pop('name', None)
            for name in location_names:
                _alternate = dict(alternate)
                if (name[:l].lower() == value):
                    _alternate['name'] = name
                    set_match_strings(_alternate, value)
                    iappend(_alternate)
            if (item['name'][:l].lower() == value):
                set_match_strings(item, value)
                iappend(item)
        output = json.dumps(items, separators=SEPARATORS)
    response.headers['Content-Type'] = 'application/json'
    return output
