'Add a conditional format 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:      Conditional format options.
Returns:
0:  Success.
-1: Row or column is out of worksheet bounds.
-2: Incorrect parameter or option.'

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

    if self._check_dimensions(first_row, first_col, True, True):
        return (-1)
    if self._check_dimensions(last_row, last_col, True, True):
        return (-1)
    if (options is None):
        options = {}
    options = options.copy()
    valid_parameter = {'type': True, 'format': True, 'criteria': True, 'value': True, 'minimum': True, 'maximum': True, 'min_type': True, 'mid_type': True, 'max_type': True, 'min_value': True, 'mid_value': True, 'max_value': True, 'min_color': True, 'mid_color': True, 'max_color': True, 'min_length': True, 'max_length': True, 'multi_range': True, 'bar_color': 1}
    for param_key in options.keys():
        if (param_key not in valid_parameter):
            warn(("Unknown    parameter    '%s'    in    conditional_formatting()" % param_key))
            return (-2)
    if ('type' not in options):
        warn("Parameter    'type'    is    required    in    conditional_formatting()")
        return (-2)
    valid_type = {'cell': 'cellIs', 'date': 'date', 'time': 'time', 'average': 'aboveAverage', 'duplicate': 'duplicateValues', 'unique': 'uniqueValues', 'top': 'top10', 'bottom': 'top10', 'text': 'text', 'time_period': 'timePeriod', 'blanks': 'containsBlanks', 'no_blanks': 'notContainsBlanks', 'errors': 'containsErrors', 'no_errors': 'notContainsErrors', '2_color_scale': '2_color_scale', '3_color_scale': '3_color_scale', 'data_bar': 'dataBar', 'formula': 'expression'}
    if (options['type'] not in valid_type):
        warn(("Unknown    validation    type    '%s'    for    parameter    'type'    in    conditional_formatting()" % options['type']))
        return (-2)
    else:
        if (options['type'] == 'bottom'):
            options['direction'] = 'bottom'
        options['type'] = valid_type[options['type']]
    criteria_type = {'between': 'between', 'not    between': 'notBetween', 'equal    to': 'equal', '=': 'equal', '==': 'equal', 'not    equal    to': 'notEqual', '!=': 'notEqual', '<>': 'notEqual', 'greater    than': 'greaterThan', '>': 'greaterThan', 'less    than': 'lessThan', '<': 'lessThan', 'greater    than    or    equal    to': 'greaterThanOrEqual', '>=': 'greaterThanOrEqual', 'less    than    or    equal    to': 'lessThanOrEqual', '<=': 'lessThanOrEqual', 'containing': 'containsText', 'not    containing': 'notContains', 'begins    with': 'beginsWith', 'ends    with': 'endsWith', 'yesterday': 'yesterday', 'today': 'today', 'last    7    days': 'last7Days', 'last    week': 'lastWeek', 'this    week': 'thisWeek', 'continue    week': 'continueWeek', 'last    month': 'lastMonth', 'this    month': 'thisMonth', 'continue    month': 'continueMonth'}
    if (('criteria' in options) and (options['criteria'] in criteria_type)):
        options['criteria'] = criteria_type[options['criteria']]
    if ((options['type'] == 'date') or (options['type'] == 'time')):
        options['type'] = 'cellIs'
        if ('value' in options):
            if (not supported_datetime(options['value'])):
                warn("Conditional    format    'value'    must    be    a    datetime    object.")
                return (-2)
            else:
                date_time = self._convert_date_time(options['value'])
                options['value'] = ('%.16g' % date_time)
        if ('minimum' in options):
            if (not supported_datetime(options['minimum'])):
                warn("Conditional    format    'minimum'    must    be    a    datetime    object.")
                return (-2)
            else:
                date_time = self._convert_date_time(options['minimum'])
                options['minimum'] = ('%.16g' % date_time)
        if ('maximum' in options):
            if (not supported_datetime(options['maximum'])):
                warn("Conditional    format    'maximum'    must    be    a    datetime    object.")
                return (-2)
            else:
                date_time = self._convert_date_time(options['maximum'])
                options['maximum'] = ('%.16g' % date_time)
    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)
    if ((first_row == last_row) and (first_col == last_col)):
        cell_range = xl_rowcol_to_cell(first_row, first_col)
        start_cell = cell_range
    else:
        cell_range = xl_range(first_row, first_col, last_row, last_col)
        start_cell = xl_rowcol_to_cell(first_row, first_col)
    if ('multi_range' in options):
        cell_range = options['multi_range']
        cell_range = cell_range.replace('$', '')
    if (('format' in options) and options['format']):
        options['format'] = options['format']._get_dxf_index()
    options['priority'] = self.dxf_priority
    self.dxf_priority += 1
    if (options['type'] == 'text'):
        if (options['criteria'] == 'containsText'):
            options['type'] = 'containsText'
            options['formula'] = ('NOT(ISERROR(SEARCH("%s",%s)))' % (options['value'], start_cell))
        elif (options['criteria'] == 'notContains'):
            options['type'] = 'notContainsText'
            options['formula'] = ('ISERROR(SEARCH("%s",%s))' % (options['value'], start_cell))
        elif (options['criteria'] == 'beginsWith'):
            options['type'] = 'beginsWith'
            options['formula'] = ('LEFT(%s,%d)="%s"' % (start_cell, len(options['value']), options['value']))
        elif (options['criteria'] == 'endsWith'):
            options['type'] = 'endsWith'
            options['formula'] = ('RIGHT(%s,%d)="%s"' % (start_cell, len(options['value']), options['value']))
        else:
            warn("Invalid    text    criteria    'options['criteria']'    in    conditional_formatting()")
    if (options['type'] == 'timePeriod'):
        if (options['criteria'] == 'yesterday'):
            options['formula'] = ('FLOOR(%s,1)=TODAY()-1' % start_cell)
        elif (options['criteria'] == 'today'):
            options['formula'] = ('FLOOR(%s,1)=TODAY()' % start_cell)
        elif (options['criteria'] == 'tomorrow'):
            options['formula'] = ('FLOOR(%s,1)=TODAY()+1' % start_cell)
        elif (options['criteria'] == 'last7Days'):
            options['formula'] = ('AND(TODAY()-FLOOR(%s,1)<=6,FLOOR(%s,1)<=TODAY())' % (start_cell, start_cell))
        elif (options['criteria'] == 'lastWeek'):
            options['formula'] = ('AND(TODAY()-ROUNDDOWN(%s,0)>=(WEEKDAY(TODAY())),TODAY()-ROUNDDOWN(%s,0)<(WEEKDAY(TODAY())+7))' % (start_cell, start_cell))
        elif (options['criteria'] == 'thisWeek'):
            options['formula'] = ('AND(TODAY()-ROUNDDOWN(%s,0)<=WEEKDAY(TODAY())-1,ROUNDDOWN(%s,0)-TODAY()<=7-WEEKDAY(TODAY()))' % (start_cell, start_cell))
        elif (options['criteria'] == 'continueWeek'):
            options['formula'] = ('AND(ROUNDDOWN(%s,0)-TODAY()>(7-WEEKDAY(TODAY())),ROUNDDOWN(%s,0)-TODAY()<(15-WEEKDAY(TODAY())))' % (start_cell, start_cell))
        elif (options['criteria'] == 'lastMonth'):
            options['formula'] = ('AND(MONTH(%s)=MONTH(TODAY())-1,OR(YEAR(%s)=YEAR(TODAY()),AND(MONTH(%s)=1,YEAR(A1)=YEAR(TODAY())-1)))' % (start_cell, start_cell, start_cell))
        elif (options['criteria'] == 'thisMonth'):
            options['formula'] = ('AND(MONTH(%s)=MONTH(TODAY()),YEAR(%s)=YEAR(TODAY()))' % (start_cell, start_cell))
        elif (options['criteria'] == 'continueMonth'):
            options['formula'] = ('AND(MONTH(%s)=MONTH(TODAY())+1,OR(YEAR(%s)=YEAR(TODAY()),AND(MONTH(%s)=12,YEAR(%s)=YEAR(TODAY())+1)))' % (start_cell, start_cell, start_cell, start_cell))
        else:
            warn("Invalid    time_period    criteria    'options['criteria']'    in    conditional_formatting()")
    if (options['type'] == 'containsBlanks'):
        options['formula'] = ('LEN(TRIM(%s))=0' % start_cell)
    if (options['type'] == 'notContainsBlanks'):
        options['formula'] = ('LEN(TRIM(%s))>0' % start_cell)
    if (options['type'] == 'containsErrors'):
        options['formula'] = ('ISERROR(%s)' % start_cell)
    if (options['type'] == 'notContainsErrors'):
        options['formula'] = ('NOT(ISERROR(%s))' % start_cell)
    if (options['type'] == '2_color_scale'):
        options['type'] = 'colorScale'
        options['format'] = None
        options['mid_type'] = None
        options['mid_color'] = None
        options.setdefault('min_type', 'min')
        options.setdefault('max_type', 'max')
        options.setdefault('min_value', 0)
        options.setdefault('max_value', 0)
        options.setdefault('min_color', '#FF7128')
        options.setdefault('max_color', '#FFEF9C')
        options['min_color'] = xl_color(options['min_color'])
        options['max_color'] = xl_color(options['max_color'])
    if (options['type'] == '3_color_scale'):
        options['type'] = 'colorScale'
        options['format'] = None
        options.setdefault('min_type', 'min')
        options.setdefault('mid_type', 'percentile')
        options.setdefault('max_type', 'max')
        options.setdefault('min_value', 0)
        options.setdefault('max_value', 0)
        options.setdefault('min_color', '#F8696B')
        options.setdefault('mid_color', '#FFEB84')
        options.setdefault('max_color', '#63BE7B')
        options['min_color'] = xl_color(options['min_color'])
        options['mid_color'] = xl_color(options['mid_color'])
        options['max_color'] = xl_color(options['max_color'])
        if ('mid_value' not in options):
            options['mid_value'] = 50
    if (options['type'] == 'dataBar'):
        options['format'] = None
        options.setdefault('min_type', 'min')
        options.setdefault('max_type', 'max')
        options.setdefault('min_value', 0)
        options.setdefault('max_value', 0)
        options.setdefault('bar_color', '#638EC6')
        options['bar_color'] = xl_color(options['bar_color'])
    if (cell_range in self.cond_formats):
        self.cond_formats[cell_range].append(options)
    else:
        self.cond_formats[cell_range] = [options]
