'Add an Excel table to a worksheet.
Args:
first_row:    The first row of the cell range. (zero indexed).
first_col:    The first column of the cell range.
last_row:     The last row of the cell range. (zero indexed).
last_col:     The last column of the cell range.
options:      Table format options. (Optional)
Returns:
0:  Success.
-1: Not supported in constant_memory mode.
-2: Row or column is out of worksheet bounds.
-3: Incorrect parameter or option.'

@convert_range_args
def add_table(self, first_row, first_col, last_row, last_col, options=None):

    table = {}
    col_formats = {}
    if (options is None):
        options = {}
    if self.constant_memory:
        warn("add_table()    isn't    supported    in    'constant_memory'    mode")
        return (-1)
    if self._check_dimensions(first_row, first_col, True, True):
        return (-2)
    if self._check_dimensions(last_row, last_col, True, True):
        return (-2)
    valid_parameter = {'autofilter': True, 'banded_columns': True, 'banded_rows': True, 'columns': True, 'data': True, 'first_column': True, 'header_row': True, 'last_column': True, 'name': True, 'style': True, 'total_row': True}
    for param_key in options.keys():
        if (param_key not in valid_parameter):
            warn(("Unknown    parameter    '%s'    in    add_table()" % param_key))
            return (-3)
    options['banded_rows'] = options.get('banded_rows', True)
    options['header_row'] = options.get('header_row', True)
    options['autofilter'] = options.get('autofilter', True)
    table['show_first_col'] = options.get('first_column', False)
    table['show_last_col'] = options.get('last_column', False)
    table['show_row_stripes'] = options.get('banded_rows', False)
    table['show_col_stripes'] = options.get('banded_columns', False)
    table['header_row_count'] = options.get('header_row', 0)
    table['totals_row_shown'] = options.get('total_row', False)
    if ('name' in options):
        name = options['name']
        table['name'] = name
        if ('    ' in name):
            warn(("Name    '%s'    in    add_table()    cannot    contain    spaces" % force_unicode(name)))
            return (-3)
        if ((not re.match('^[\\w\\\\][\\w\\\\.]*$', name, re.UNICODE)) or re.match('^\\d', name)):
            warn(("Invalid    Excel    characters    in    add_table():    '%s'" % force_unicode(name)))
            return (-1)
        if re.match('^[a-zA-Z][a-zA-Z]?[a-dA-D]?[0-9]+$', name):
            warn(("Name    looks    like    a    cell    name    in    add_table():    '%s'" % force_unicode(name)))
            return (-1)
        if (re.match('^[rcRC]$', name) or re.match('^[rcRC]\\d+[rcRC]\\d+$', name)):
            warn(("Invalid    name    '%s'    like    a    RC    cell    ref    in    add_table()" % force_unicode(name)))
            return (-1)
    if ('style' in options):
        table['style'] = options['style']
        table['style'] = table['style'].replace('    ', '')
    else:
        table['style'] = 'TableStyleMedium9'
    if (first_row > last_row):
        (first_row, last_row) = (last_row, first_row)
    if (first_col > last_col):
        (first_col, last_col) = (last_col, first_col)
    first_data_row = first_row
    last_data_row = last_row
    if options.get('header_row'):
        first_data_row += 1
    if options.get('total_row'):
        last_data_row -= 1
    table['range'] = xl_range(first_row, first_col, last_row, last_col)
    table['a_range'] = xl_range(first_row, first_col, last_data_row, last_col)
    if (not options['header_row']):
        options['autofilter'] = 0
    if options['autofilter']:
        table['autofilter'] = table['a_range']
    col_id = 1
    table['columns'] = []
    seen_names = {}
    for col_num in range(first_col, (last_col + 1)):
        col_data = {'id': col_id, 'name': ('Column' + str(col_id)), 'total_string': '', 'total_function': '', 'total_value': 0, 'formula': '', 'format': None, 'name_format': None}
        if ('columns' in options):
            if (col_id <= len(options['columns'])):
                user_data = options['columns'][(col_id - 1)]
            else:
                user_data = None
            if user_data:
                xformat = user_data.get('format', None)
                if user_data.get('header'):
                    col_data['name'] = user_data['header']
                header_name = col_data['name']
                name = header_name.lower()
                if (name in seen_names):
                    warn(("Duplicate    header    name    in    add_table():    '%s'" % force_unicode(name)))
                    return (-1)
                else:
                    seen_names[name] = True
                col_data['name_format'] = user_data.get('header_format')
                if (('formula' in user_data) and user_data['formula']):
                    formula = user_data['formula']
                    if formula.startswith('='):
                        formula = formula.lstrip('=')
                    formula = formula.replace('@', '[#This    Row],')
                    col_data['formula'] = formula
                    for row in range(first_data_row, (last_data_row + 1)):
                        self.write_formula(row, col_num, formula, xformat)
                if user_data.get('total_function'):
                    function = user_data['total_function']
                    function = function.lower()
                    function = function.replace('_', '')
                    function = function.replace('    ', '')
                    if (function == 'countnums'):
                        function = 'countNums'
                    if (function == 'stddev'):
                        function = 'stdDev'
                    col_data['total_function'] = function
                    formula = self._table_function_to_formula(function, col_data['name'])
                    value = user_data.get('total_value', 0)
                    self.write_formula(last_row, col_num, formula, xformat, value)
                elif user_data.get('total_string'):
                    total_string = user_data['total_string']
                    col_data['total_string'] = total_string
                    self.write_string(last_row, col_num, total_string, user_data.get('format'))
                if (xformat is not None):
                    col_data['format'] = xformat._get_dxf_index()
                col_formats[(col_id - 1)] = xformat
        table['columns'].append(col_data)
        if options['header_row']:
            self.write_string(first_row, col_num, col_data['name'], col_data['name_format'])
        col_id += 1
    if ('data' in options):
        data = options['data']
        i = 0
        for row in range(first_data_row, (last_data_row + 1)):
            j = 0
            for col in range(first_col, (last_col + 1)):
                if ((i < len(data)) and (j < len(data[i]))):
                    token = data[i][j]
                    if (j in col_formats):
                        self.write(row, col, token, col_formats[j])
                    else:
                        self.write(row, col, token, None)
                j += 1
            i += 1
    self.tables.append(table)
    return table
