'Generate a data table.
@param r: the S3Request instance
@param widget: the widget definition as dict
@param attr: controller attributes for the request
@todo: fix export formats'

def _datatable(self, r, widget, **attr):

    widget_get = widget.get
    context = widget_get('context')
    tablename = widget_get('tablename')
    (resource, context) = self._resolve_context(r, tablename, context)
    list_fields = widget_get('list_fields')
    if (not list_fields):
        list_fields = resource.list_fields()
    widget_filter = widget_get('filter')
    if widget_filter:
        resource.add_filter(widget_filter)
    list_id = ('profile-list-%s-%s' % (tablename, widget['index']))
    def default_orderby():
        for f in list_fields:
            selector = (f[1] if isinstance(f, tuple) else f)
            if (selector == 'id'):
                continue
            rfield = resource.resolve_selector(selector)
            if rfield.field:
                return rfield.field
        return None
    representation = r.representation
    get_vars = self.request.get_vars
    if (representation == 'aadata'):
        start = get_vars.get('displayStart', None)
        limit = get_vars.get('pageLength', 0)
    else:
        start = get_vars.get('start', None)
        limit = get_vars.get('limit', 0)
    if limit:
        if (limit.lower() == 'none'):
            limit = None
        else:
            try:
                start = int(start)
                limit = int(limit)
            except (ValueError, TypeError):
                start = None
                limit = 0
    else:
        start = None
    dtargs = attr.get('dtargs', {})
    if r.interactive:
        s3 = current.response.s3
        if s3.dataTable_pageLength:
            display_length = s3.dataTable_pageLength
        else:
            display_length = widget.get('pagesize', 10)
        dtargs['dt_lengthMenu'] = [[10, 25, 50, (-1)], [10, 25, 50, s3_str(current.T('All'))]]
        orderby = widget_get('orderby')
        if (not orderby):
            orderby = resource.get_config('orderby')
        if (not orderby):
            orderby = default_orderby()
        if (not s3.no_sspag):
            dt_pagination = 'true'
            if ((not limit) and (display_length is not None)):
                limit = (2 * display_length)
            else:
                limit = None
        else:
            dt_pagination = 'false'
        (dt, totalrows, ids) = resource.datatable(fields=list_fields, start=start, limit=limit, orderby=orderby)
        displayrows = totalrows
        if dt.empty:
            empty_str = self.crud_string(tablename, 'msg_list_empty')
        else:
            empty_str = self.crud_string(tablename, 'msg_no_match')
        empty = DIV(empty_str, _class='empty')
        dtargs['dt_pagination'] = dt_pagination
        dtargs['dt_pageLength'] = display_length
        s3.no_formats = True
        dtargs['dt_base_url'] = r.url(method='', vars={})
        dtargs['dt_ajax_url'] = r.url(vars={'update': widget['index']}, representation='aadata')
        actions = widget_get('actions')
        if callable(actions):
            actions = actions(r, list_id)
        if actions:
            dtargs['dt_row_actions'] = actions
        datatable = dt.html(totalrows, displayrows, id=list_id, **dtargs)
        if dt.data:
            empty.update(_style='display:none')
        else:
            datatable.update(_style='display:none')
        contents = DIV(datatable, empty, _class='dt-contents')
        create_popup = self._create_popup(r, widget, list_id, resource, context, totalrows)
        label = widget_get('label', '')
        if label:
            label = current.T(label)
        else:
            label = self.crud_string(tablename, 'title_list')
        icon = widget_get('icon', '')
        if icon:
            icon = ICON(icon)
        _class = self._lookup_class(r, widget)
        output = DIV(create_popup, H4(icon, label, _class='profile-sub-header'), DIV(contents, _class='card-holder'), _class=_class)
        return output
    elif (representation == 'aadata'):
        (searchq, orderby, left) = resource.datatable_filter(list_fields, get_vars)
        if (not orderby):
            orderby = widget_get('orderby')
        if (not orderby):
            orderby = resource.get_config('orderby')
        if (not orderby):
            orderby = default_orderby()
        if (searchq is not None):
            totalrows = resource.count()
            resource.add_filter(searchq)
        else:
            totalrows = None
        if (totalrows != 0):
            (dt, displayrows, ids) = resource.datatable(fields=list_fields, start=start, limit=limit, left=left, orderby=orderby, getids=False)
        else:
            (dt, displayrows) = (None, 0)
        if (totalrows is None):
            totalrows = displayrows
        draw = int((get_vars.get('draw') or 0))
        if (dt is not None):
            data = dt.json(totalrows, displayrows, list_id, draw, **dtargs)
        else:
            data = ('{"recordsTotal":%s,"recordsFiltered":0,"dataTable_id":"%s","draw":%s,"data":[]}' % (totalrows, list_id, draw))
        return data
    else:
        r.error(415, current.ERROR.BAD_FORMAT)
