'Parse datatable search/sort vars into a tuple of
query, orderby and left joins
@param fields: list of field selectors representing
the order of fields in the datatable (list_fields)
@param get_vars: the datatable GET vars
@return: tuple of (query, orderby, left joins)'

def datatable_filter(self, fields, get_vars):

    db = current.db
    left_joins = S3Joins(self.tablename)
    sSearch = 'sSearch'
    iColumns = 'iColumns'
    iSortingCols = 'iSortingCols'
    parent = self.parent
    fkey = self.fkey
    if (self.linked is not None):
        skip = self.linked.tablename
    else:
        skip = None
    rfields = self.resolve_selectors(fields)[0]
    searchq = None
    if ((sSearch in get_vars) and (iColumns in get_vars)):
        text = get_vars[sSearch]
        words = [w for w in text.lower().split()]
        if words:
            try:
                numcols = int(get_vars[iColumns])
            except ValueError:
                numcols = 0
            flist = []
            for i in xrange(numcols):
                try:
                    rfield = rfields[i]
                    field = rfield.field
                except (KeyError, IndexError):
                    continue
                if (field is None):
                    if hasattr(rfield, 'search_field'):
                        field = db[rfield.tname][rfield.search_field]
                    else:
                        continue
                ftype = str(field.type)
                left_joins.extend(rfield.left)
                if ((ftype[:9] == 'reference') and hasattr(field, 'sortby') and field.sortby):
                    tn = ftype[10:]
                    if ((parent is not None) and (parent.tablename == tn) and (field.name != fkey)):
                        alias = ('%s_%s_%s' % (parent.prefix, 'linked', parent.name))
                        ktable = db[tn].with_alias(alias)
                        ktable._id = ktable[ktable._id.name]
                        tn = alias
                    elif (tn == field.tablename):
                        (prefix, name) = field.tablename.split('_', 1)
                        alias = ('%s_%s_%s' % (prefix, field.name, name))
                        ktable = db[tn].with_alias(alias)
                        ktable._id = ktable[ktable._id.name]
                        tn = alias
                    else:
                        ktable = db[tn]
                    if (tn != skip):
                        left_joins.add(ktable.on((field == ktable._id)))
                    if isinstance(field.sortby, (list, tuple)):
                        flist.extend([ktable[f] for f in field.sortby if (f in ktable.fields)])
                    elif (field.sortby in ktable.fields):
                        flist.append(ktable[field.sortby])
                else:
                    flist.append(field)
        opts = Storage()
        queries = []
        for w in words:
            wqueries = []
            for field in flist:
                ftype = str(field.type)
                options = None
                fname = str(field)
                if (fname in opts):
                    options = opts[fname]
                elif (ftype[:7] in ('integer', 'list:in', 'list:st', 'referen', 'list:re', 'string')):
                    requires = field.requires
                    if (not isinstance(requires, (list, tuple))):
                        requires = [requires]
                    if requires:
                        r = requires[0]
                        if isinstance(r, IS_EMPTY_OR):
                            r = r.other
                        if hasattr(r, 'options'):
                            try:
                                options = r.options()
                            except:
                                options = []
                if ((options is None) and (ftype in ('string', 'text'))):
                    wqueries.append(field.lower().like(('%%%s%%' % w)))
                elif (options is not None):
                    opts[fname] = options
                    vlist = [v for (v, t) in options if (s3_unicode(t).lower().find(s3_unicode(w)) != (-1))]
                    if vlist:
                        wqueries.append(field.belongs(vlist))
            if len(wqueries):
                queries.append(reduce((lambda x, y: ((x | y) if (x is not None) else y)), wqueries))
        if len(queries):
            searchq = reduce((lambda x, y: ((x & y) if (x is not None) else y)), queries)
    orderby = []
    if (iSortingCols in get_vars):
        def direction(i):
            sort_dir = get_vars[('sSortDir_%s' % str(i))]
            return ((sort_dir and ('    %s' % sort_dir)) or '')
        try:
            numcols = int(get_vars[iSortingCols])
        except:
            numcols = 0
        columns = []
        pkey = str(self._id)
        for i in xrange(numcols):
            try:
                iSortCol = int(get_vars[('iSortCol_%s' % i)])
            except (AttributeError, KeyError):
                columns.append(Storage(field=None))
                continue
            for j in xrange(iSortCol):
                if (get_vars.get(('bSortable_%s' % j), 'true') == 'false'):
                    try:
                        if (rfields[j].colname != pkey):
                            iSortCol -= 1
                    except KeyError:
                        break
            try:
                rfield = rfields[iSortCol]
            except IndexError:
                columns.append(Storage(field=None))
            else:
                columns.append(rfield)
        for i in xrange(len(columns)):
            rfield = columns[i]
            field = rfield.field
            if (field is None):
                continue
            ftype = str(field.type)
            represent = field.represent
            if ((not hasattr(represent, 'skip_dt_orderby')) and hasattr(represent, 'dt_orderby')):
                field.represent.dt_orderby(field, direction(i), orderby, left_joins)
            elif ((ftype[:9] == 'reference') and hasattr(field, 'sortby') and field.sortby):
                tn = ftype[10:]
                if ((parent is not None) and (parent.tablename == tn) and (field.name != fkey)):
                    alias = ('%s_%s_%s' % (parent.prefix, 'linked', parent.name))
                    ktable = db[tn].with_alias(alias)
                    ktable._id = ktable[ktable._id.name]
                    tn = alias
                elif (tn == field.tablename):
                    (prefix, name) = field.tablename.split('_', 1)
                    alias = ('%s_%s_%s' % (prefix, field.name, name))
                    ktable = db[tn].with_alias(alias)
                    ktable._id = ktable[ktable._id.name]
                    tn = alias
                else:
                    ktable = db[tn]
                if (tn != skip):
                    left_joins.extend(rfield.left)
                    left_joins.add(ktable.on((field == ktable._id)))
                if (not isinstance(field.sortby, (list, tuple))):
                    orderby.append(('%s.%s%s' % (tn, field.sortby, direction(i))))
                else:
                    orderby.append(',    '.join([('%s.%s%s' % (tn, fn, direction(i))) for fn in field.sortby]))
            else:
                orderby.append(('%s%s' % (field, direction(i))))
    if orderby:
        orderby = ',    '.join(orderby)
    else:
        orderby = None
    left_joins = left_joins.as_list(tablenames=left_joins.joins.keys())
    return (searchq, orderby, left_joins)
