"""
Creates renderer dictionaries that can be used to help visualize webmap content
"""

import json

import arcgis
from arcgis._impl.common._utils import chunks
from arcgis.features import FeatureCollection, FeatureSet, SpatialDataFrame
from arcgis.gis import GIS
from arcgis.geometry import _types
from arcgis.mapping._utils import _get_list_value
from arcgis.mapping.symbol import create_symbol, _cmap2rgb

__all__ = ['generate_renderer']

RENDERER_TYPES = {
    "s" : 'simple',#
    "u" : 'unique',#
    "u-a" : 'unique',#
    'h' : 'heatmap',#
    'c' : 'ClassBreaks',#
    "p" : 'Predominance',
    'str' : 'Stretch',#
    't' : "Temporal",#
    'v' : "vector field"#
}
def _size_info(field, min_value, max_value,
               min_size=6, max_size=37.5, unit='unknown'):
    """
    """
    return {
         'type' : 'sizeInfo',
          'field' : field,
          'valueUnit' : unit,
          'minSize' : min_size,
          'maxSize' : max_size,
          'minDataValue' : min_value,
          'maxDataValue' : max_value
     }

def _color_info(field:str,
                values:list,
                steps:int=6,
                colors:str='Reds_r' )-> dict:
    """
    Creates the Color Infomration Visual Variable from a collection of information.

    """
    d = {
         'type' : 'colorInfo',
          'field' : field,
          'stops' : []
     }
    import numpy as np
    steps = np.linspace(0,254, steps, endpoint=True, dtype=int).tolist()

    cmaps = [_cmap2rgb(colors, step) for step in steps]
    uvalues = list(set(values))
    sorted(uvalues)
    psteps = uvalues[::int(len(uvalues)/len(steps))]
    if psteps[-1] != uvalues[-1]:
        psteps.append(uvalues[-1])
        steps = np.linspace(0,254, len(steps)+ 1, endpoint=True, dtype=int).tolist()
        cmaps = [_cmap2rgb(colors, step) for step in steps]

    #value_index = [uvalues[int(s*step_size)] for s in range(steps)]
    for k,v in dict(zip(psteps, cmaps)).items():
        d['stops'].append(
              {
                   'value' : k,
                    'color' : v,
                    'label' : None
               }
          )
    return d
#--------------------------------------------------------------------------
def _trans_info(data, **kwargs):
    """creates a transparancy information for visual variables"""
    ti = None
    if 'trans_info_field' in symbol_args:
        ti = {}

        ti['field'] = kwargs.pop('trans_info_field', None)
        ti['normalizationField'] = kwargs.pop('trans_norm_field', None)
        stops = kwargs.pop('trans_stops', None)
        if stops is None and \
           data:
            stops = []
            transp = 100/len(data)
            for d in data:

                stops.append({
                    "stop": {
                        "value": d,
                        "transparency": transp
                    }
                })
                transp += transp
                del d
            del data
        elif data is None and stops is None:
            raise Exception("Cannot create transparancy info.")

        ti['stops'] = stops
        ti['type'] = 'transparancyInfo'
        ti['valueExpression'] = kwargs.pop('trans_value_exp', None)
        ti['valueExpressionTitle'] = kwargs.pop('trans_exp_title', None)
    return ti
#--------------------------------------------------------------------------
def _si_creator(**kwargs):
    """creates the size information from key/value pairs"""
    si = {
        'type' : 'sizeInfo'
    }
    if 'si_field' in kwargs or \
       'si_minSize' in kwargs:
        si['expression'] = kwargs.pop('si_expresion', 'view.scale')
        si['field'] = kwargs.pop('si_field', None)
        si['maxDataValue'] = kwargs.pop('si_max_data_value', None)
        si['maxSize'] = kwargs.pop('si_max_size', 20)
        si['minDatavalue'] = kwargs.pop('si_min_data_value', None)
        si['minSize'] = kwargs.pop('si_min_size', 10)

        si['normalizationField'] = kwargs.pop('si_norm_field', None)
        si['stops'] = kwargs.pop('si_stops', None)
        if 'si_target' in kwargs:
            si['target'] = kwargs.pop('si_target', None)
        si['valueExpression'] = kwargs.pop('si_expression', None)
        si['valueExpressionTitle'] = kwargs.pop('si_expression_title', None)
        si['valueUnit'] = kwargs.pop('si_value_unit', None)
        return si
    else:
        return None
    return None
#--------------------------------------------------------------------------
def _ri_creator(**kwargs):
    """creates rotation information for visual variables"""
    ri = {
        'type' : 'rotationInfo'
    }
    if 'ri_type' in kwargs:
        ri['rotatationType'] = kwargs.pop('ri_type')
        ri['valueExpression'] = kwargs.pop('ri_expression', None)
        ri['valueExpressionTitle'] = kwargs.pop('ri_expression_title', 'ri_title')
        ri['field'] = kwargs.pop('ri_field', None)

        return ri
    return None
#--------------------------------------------------------------------------
def visual_variables(geometry_type, sdf_or_list, **kwargs):
    """
    a function to create visual variables

    This operation allows developers to take a deep dive into developing custom renderer.
    Here a user/developer can create transparancy, size information, and other rules to
    improve the overall feel and look of spatial information on a map.

    Each type of information is detailed in the tables below.

    ======================  =========================================================
    **optional variables**  **description**
    ----------------------  ---------------------------------------------------------
    trans_info_field        Attribute field used for setting the transparency of a
                            feature if no trans_value_exp is provided.
    ----------------------  ---------------------------------------------------------
    trans_norm_field        Attribute field used to normalize the data.
    ----------------------  ---------------------------------------------------------
    trans_stops             An array of transparency stop objects.
    ----------------------  ---------------------------------------------------------
    trans_value_exp         An Arcade expression evaluating to a number.
    ----------------------  ---------------------------------------------------------
    trans_exp_title         The title identifying and describing the associated
                            Arcade expression as defined in the valueExpression
                            property.
    ----------------------  ---------------------------------------------------------

    **Size Info Visual Variable**

    The size Info visual variable defines how size is applied to features based on the
    values of a numeric field attribute. The minimum and maximum values of the data
    should be indicated along with their respective size values. You must specify
    minSize and maxSize or stops to construct the size ramp. All features with values
    falling in between the specified min and max data values (or stops) will be scaled
    proportionally between the provided min and max sizes.

    ======================  =========================================================
    **arguements**          **description**
    ----------------------  ---------------------------------------------------------
    si_field                Attribute field used for size rendering if no
                            si_expression is provided.
    ----------------------  ---------------------------------------------------------
    si_max_data_value       The maximum data value.
    ----------------------  ---------------------------------------------------------
    si_max_size             Specifies the largest marker size to use at any given map
                            scale. Can be either a fixed number or object, depending
                            on whether the user chose a fixed range or not.
    ----------------------  ---------------------------------------------------------
    si_min_data_value       The minimum data value.
    ----------------------  ---------------------------------------------------------
    si_min_size             Specifies the smallest marker size to use at any given
                            map scale. Can be either a fixed number or object,
                            depending on whether the user chose a fixed range or not.
    ----------------------  ---------------------------------------------------------
    si_norm_field           Attribute field used to normalize the data.
    ----------------------  ---------------------------------------------------------
    si_stops                An array of objects that defines the thematic size ramp
                            in a sequence of data or expression stops.
    ----------------------  ---------------------------------------------------------
    si_target               Only used when sizeInfo is used for polygon outlines.
                            Value of this property must be outline
    ----------------------  ---------------------------------------------------------
    si_expression           An Arcade expression evaluating to a number
    ----------------------  ---------------------------------------------------------
    si_expression_title     the title identifying and describing the associated
                            Arcade expression
    ----------------------  ---------------------------------------------------------
    si_value_unit           A string value indicating the required unit of measurement.
    ======================  =========================================================

    ======================  =========================================================
    **arguements**          **description**
    ----------------------  ---------------------------------------------------------
    ri_field                Attribute field used for setting the rotation of a symbol
                            if no ri_expression is provided.
    ----------------------  ---------------------------------------------------------
    ri_type                 Defines the origin and direction of rotation depending on
                            how the angle of rotation was measured. Possible values
                            are geographic which rotates the symbol from the north in
                            a clockwise direction and arithmetic which rotates the
                            symbol from the east in a counter-clockwise direction.
                            Must be one of the following values:

                            - geographic
                            - arithmetic

    ----------------------  ---------------------------------------------------------
    ri_expression           An Arcade expression evaluating to a number.
    ----------------------  ---------------------------------------------------------
    ri_expression_title     The title identifying and describing the ri_expression
    ======================  =========================================================



    """
    v = []
    if isinstance(sdf_or_list, SpatialDataFrame) and \
       'trans_info_field' in kwargs:

        data = sdf_or_list[trans_info_field].unique().tolist()
    elif isinstance(sdf_or_list, (tuple, list)):
        data = list(set(sdf_or_list))

    ti = _trans_info(data=data, **kwargs)
    if ti:
        v.append(ti)
    si = _si_creator(**kwargs)
    if si:
        v.append(si)
    ri = _ri_creator(**kwargs)
    if ri:
        v.append(ri)
    return v

#--------------------------------------------------------------------------
def generate_renderer(geometry_type,
                      sdf_or_series=None,
                      label=None,
                      render_type=None,
                      colors=None,
                      **symbol_args):
    """
    Generates the Renderer JSON


    ======================  =========================================================
    **Explicit Argument**   **Description**
    ----------------------  ---------------------------------------------------------
    geometry_type           required string. The allowed values are: Point, Polyline,
                            Polygon, or Raster. This required parameter is used to
                            help ensure the requested renderer is valid for the
                            specific type of geometry.

                            =============   ========================================
                            **Geometry**    **Supported Renderer Types**
                            -------------   ----------------------------------------
                            Point           simple, unique, class breaks, heat map,
                                            temporal
                            -------------   ----------------------------------------
                            Polyline        simple, unique, class break
                            -------------   ----------------------------------------
                            Polygon         simple, unique, class break
                            -------------   ----------------------------------------
                            Raster          stretched
                            =============   ========================================

                            The table above provides a quick summary based on the
                            allowed renderer types based on the geometry.

    ----------------------  ---------------------------------------------------------
    sdf_or_series           optional SpatialDataFrame/Pandas Series/GeoSeries. The
                            spatial dataset to render.
    ----------------------  ---------------------------------------------------------
    label                   optional string. Name of the layer in the TOC/Legend
    ----------------------  ---------------------------------------------------------
    render_type             optional string.  Determines the type of renderer to use
                            for the provided dataset. The default is 's' which is for
                            simple renderers.

                            Allowed values:

                            + 's' - is a simple renderer that uses one symbol only.
                            + 'u' - unique renderer symbolizes features based on one
                                    or more matching string attributes.
                            + 'u-a' - unique renderer symbolizes features based on
                                      an arcade expression.
                            + 'c' - A class breaks renderer symbolizes based on the
                                    value of some numeric attribute.
                            + 'h' - heatmap renders point data into a raster
                                    visualization that emphasizes areas of higher
                                    density or weighted values.
    ----------------------  ---------------------------------------------------------
    colors                  optional string/list.  Color mapping.  For simple renderer,
                            just provide a string.  For more robust renderers like
                            unique renderer, a list can be given.
    ======================  =========================================================

    ** Simple Renderer**

    A simple renderer is a renderer that uses one symbol only.

    ======================  =========================================================
    **Optional Argument**   **Description**
    ----------------------  ---------------------------------------------------------
    symbol_type             optional string. This is the type of symbol the user
                            needs to create.  Valid inputs are: simple, picture, text,
                            or carto.  The default is simple.
    ----------------------  ---------------------------------------------------------
    symbol_style            optional string. This is the symbology used by the
                            geometry.  For example 's' for a Line geometry is a solid
                            line. And '-' is a dash line.

                            **Point Symbols**

                            + 'o' - Circle (default)
                            + '+' - Cross
                            + 'D' - Diamond
                            + 's' - Square
                            + 'x' - X

                            **Polyline Symbols**

                            + 's' - Solid (default)
                            + '-' - Dash
                            + '-.' - Dash Dot
                            + '-..' - Dash Dot Dot
                            + '.' - Dot
                            + '--' - Long Dash
                            + '--.' - Long Dash Dot
                            + 'n' - Null
                            + 's-' - Short Dash
                            + 's-.' - Short Dash Dot
                            + 's-..' - Short Dash Dot Dot
                            + 's.' - Short Dot

                            **Polygon Symbols**

                            + 's' - Solid Fill (default)
                            + '\' - Backward Diagonal
                            + '/' - Forward Diagonal
                            + '|' - Vertical Bar
                            + '-' - Horizontal Bar
                            + 'x' - Diagonal Cross
                            + '+' - Cross
    ----------------------  ---------------------------------------------------------
    description             Description of the renderer.
    ----------------------  ---------------------------------------------------------
    rotation_expression     A constant value or an expression that derives the angle
                            of rotation based on a feature attribute value. When an
                            attribute name is specified, it's enclosed in square
                            brackets.
    ----------------------  ---------------------------------------------------------
    rotation_type           String value which controls the origin and direction of
                            rotation on point features. If the rotationType is
                            defined as arithmetic, the symbol is rotated from East in
                            a counter-clockwise direction where East is the 0 degree
                            axis. If the rotationType is defined as geographic, the
                            symbol is rotated from North in a clockwise direction
                            where North is the 0 degree axis.

                            Must be one of the following values:

                            + arithmetic
                            + geographic

    ----------------------  ---------------------------------------------------------
    visual_variables        An array of objects used to set rendering properties.
    ======================  =========================================================

    **Heatmap Renderer**

    The HeatmapRenderer renders point data into a raster visualization that emphasizes
    areas of higher density or weighted values.

    ======================  =========================================================
    **Optional Argument**   **Description**
    ----------------------  ---------------------------------------------------------
    blur_radius             The radius (in pixels) of the circle over which the
                            majority of each point's value is spread.
    ----------------------  ---------------------------------------------------------
    field                   This is optional as this renderer can be created if no
                            field is specified. Each feature gets the same
                            value/importance/weight or with a field where each
                            feature is weighted by the field's value.
    ----------------------  ---------------------------------------------------------
    max_intensity           The pixel intensity value which is assigned the final
                            color in the color ramp.
    ----------------------  ---------------------------------------------------------
    min_intensity           The pixel intensity value which is assigned the initial
                            color in the color ramp.
    ----------------------  ---------------------------------------------------------
    ratio                   A number between 0-1. Describes what portion along the
                            gradient the colorStop is added.
    ======================  =========================================================

    **Predominance/Unique Renderer**

    This renderer symbolizes features based on one or more matching string attributes.

    ======================  =========================================================
    **Optional Argument**   **Description**
    ----------------------  ---------------------------------------------------------
    background_fill_symbol  A symbol used for polygon features as a background if the
                            renderer uses point symbols, e.g. for bivariate types &
                            size rendering. Only applicable to polygon layers.
                            PictureFillSymbols can also be used outside of the Map
                            Viewer for Size and Predominance and Size renderers.
    ----------------------  ---------------------------------------------------------
    default_label           Default label for the default symbol used to draw
                            unspecified values.
    ----------------------  ---------------------------------------------------------
    default_symbol          Symbol used when a value cannot be matched.
    ----------------------  ---------------------------------------------------------
    field1, field2, field3  Attribute field renderer uses to match values.
    ----------------------  ---------------------------------------------------------
    field_delimiter         String inserted between the values if multiple attribute
                            fields are specified.
    ----------------------  ---------------------------------------------------------
    rotation_expression     A constant value or an expression that derives the angle
                            of rotation based on a feature attribute value. When an
                            attribute name is specified, it's enclosed in square
                            brackets. Rotation is set using a visual variable of type
                            rotation info with a specified field or value expression
                            property.
    ----------------------  ---------------------------------------------------------
    rotation_type           String property which controls the origin and direction
                            of rotation. If the rotation type is defined as
                            arithmetic the symbol is rotated from East in a
                            counter-clockwise direction where East is the 0 degree
                            axis. If the rotation type is defined as geographic, the
                            symbol is rotated from North in a clockwise direction
                            where North is the 0 degree axis.
                            Must be one of the following values:

                            + arithmetic
                            + geographic

    ----------------------  ---------------------------------------------------------
    arcade_expression       An Arcade expression evaluating to either a string or a
                            number.
    ----------------------  ---------------------------------------------------------
    arcade_title            The title identifying and describing the associated
                            Arcade expression as defined in the valueExpression
                            property.
    ----------------------  ---------------------------------------------------------
    visual_variables        An array of objects used to set rendering properties.
    ----------------------  ---------------------------------------------------------
    unique_values           Optional list of dictionaries.  If you want to define the
                            unique values listed in the renderer, specify the list
                            using this variable.  The format of each unique value is
                            as follows:

                            {
                                "value" : <value>,
                                "label" : <label value>,
                                "description" : <optional text description>,
                                "symbol" : {...symbol...}
                            }

    ======================  =========================================================

    **Class Breaks Renderer**

    A class breaks renderer symbolizes based on the value of some numeric attribute.

    ======================  =========================================================
    **Optional Argument**   **Description**
    ----------------------  ---------------------------------------------------------
    background_fill_symbol  A symbol used for polygon features as a background if the
                            renderer uses point symbols, e.g. for bivariate types &
                            size rendering. Only applicable to polygon layers.
                            PictureFillSymbols can also be used outside of the Map
                            Viewer for Size and Predominance and Size renderers.
    ----------------------  ---------------------------------------------------------
    default_label           Default label for the default symbol used to draw
                            unspecified values.
    ----------------------  ---------------------------------------------------------
    default_symbol          Symbol used when a value cannot be matched.
    ----------------------  ---------------------------------------------------------
    method                  Determines the classification method that was used to
                            generate class breaks.

                            Must be one of the following values:

                            + esriClassifyDefinedInterval
                            + esriClassifyEqualInterval
                            + esriClassifyGeometricalInterval
                            + esriClassifyNaturalBreaks
                            + esriClassifyQuantile
                            + esriClassifyStandardDeviation
                            + esriClassifyManual

    ----------------------  ---------------------------------------------------------
    field                   Attribute field used for renderer.
    ----------------------  ---------------------------------------------------------
    min_value               The minimum numeric data value needed to begin class
                            breaks.
    ----------------------  ---------------------------------------------------------
    normalization_field     Used when normalizationType is field. The string value
                            indicating the attribute field by which the data value is
                            normalized.
    ----------------------  ---------------------------------------------------------
    normalization_total     Used when normalizationType is percent-of-total, this
                            number property contains the total of all data values.
    ----------------------  ---------------------------------------------------------
    normalization_type      Determine how the data was normalized.

                            Must be one of the following values:

                            + esriNormalizeByField
                            + esriNormalizeByLog
                            + esriNormalizeByPercentOfTotal
    ----------------------  ---------------------------------------------------------
    rotation_expression     A constant value or an expression that derives the angle
                            of rotation based on a feature attribute value. When an
                            attribute name is specified, it's enclosed in square
                            brackets.
    ----------------------  ---------------------------------------------------------
    rotation_type           A string property which controls the origin and direction
                            of rotation. If the rotation_type is defined as
                            arithmetic, the symbol is rotated from East in a
                            couter-clockwise direction where East is the 0 degree
                            axis. If the rotationType is defined as geographic, the
                            symbol is rotated from North in a clockwise direction
                            where North is the 0 degree axis.

                            Must be one of the following values:

                            + arithmetic
                            + geographic

    ----------------------  ---------------------------------------------------------
    arcade_expression       An Arcade expression evaluating to a number.
    ----------------------  ---------------------------------------------------------
    arcade_title            The title identifying and describing the associated
                            Arcade expression as defined in the arcade_expression
                            property.
    ----------------------  ---------------------------------------------------------
    visual_variables        An object used to set rendering options.
    ======================  =========================================================



    :returns: dict

    """
    import numpy as np
    if colors is None:
        colors = 'jet'
    if isinstance(colors, str):
        colors = colors.split(',')
    elif isinstance(colors, list) and \
         len(colors) == 4 and \
         all([isinstance(i, int) for i in colors]):
        colors = [colors]
    if 'alpha' in symbol_args:
        alpha = symbol_args['alpha']
    else:
        alpha = 1

    renderer = None
    vv = []
    if 'size_field' in symbol_args:
        size_field = symbol_args.pop('size_field')
        vv.append(_size_info(field=size_field,
                             min_value=sdf_or_series[size_field].min(),
                             max_value=sdf_or_series[size_field].max(),
                             min_size=symbol_args.pop('min_size', 1),
                             max_size=symbol_args.pop('max_size', 24.1),
                             unit=symbol_args.pop('size_units', 'unknown')))
    if 'ci_field' in symbol_args:
        color_field = symbol_args.pop('ci_field')
        vv.append(_color_info(field=color_field,
                              values=sdf_or_series[color_field],
                              steps=symbol_args.pop('ci_steps', 3),
                              colors=symbol_args.pop('ci_color', 'Reds_r')))
    if 'opacity_expression' in symbol_args and \
       'opacity_stops' in symbol_args:
        vv.append({
            'type': "transparencyInfo",
            'valueExpression': symbol_args.pop('opacity_expression'),
            'valueExpressionTitle': "Opacity Expression",
            'stops': symbol_args.pop("opacity_stops")
          })
    if render_type is None and \
       str(geometry_type).lower() != 'raster':
        render_type = 's'
    elif render_type is None and \
        str(geometry_type).lower() == 'raster':
        render_type = 'str'
    elif render_type.lower() not in RENDERER_TYPES:
        raise Exception("Invalid Renderer type.")
    else:
        render_type = render_type.lower()
    if render_type == 's':
        symbol = symbol_args.pop('symbol', None)
        if symbol is None:
            symbol = create_symbol(
                geometry_type=geometry_type.lower(),
                symbol_type=symbol_args.pop('symbol_type', None),
                symbol_style=symbol_args.pop('symbol_style', None),
                colors=colors[0],
                **symbol_args)
        renderer = {
            "type" : "simple",
            'label' : label,
            "description" : symbol_args.pop('description', ""),
            "rotationExpression" : symbol_args.pop("rotation_expression", ""),
            "rotationType" : symbol_args.pop("rotation_type", 'arithmetic'),
            "visualVariables" : symbol_args.pop("visual_variables", vv),
            "symbol" : symbol
        }
        return renderer
    elif render_type.lower() == "h":
        if sdf_or_series is None and \
           'field' in symbol_args:
            raise ValueError("sdf_or_series must be a Pandas' Series, SpatialDataFrame" + \
                             " or Pandas DataFrame for this type of renderer")
        colorStops = []
        field = symbol_args.pop('field', None)
        stops = symbol_args.pop('stops', 3)
        ratio = symbol_args.pop('ratio', .01)
        show_none = symbol_args.pop('show_none', False)
        maxPixelIntensity = symbol_args.pop('max_intensity', 10)
        minPixelIntensity = symbol_args.pop('min_intensity', 0)
        r = 0
        if stops < 3:
            stops = 3
        ratios = np.linspace(0,1, num=stops)
        for idx, cstep in enumerate(np.linspace(0,255,
                                 num=stops,
                                 dtype=np.int).tolist()):
            if r == 0 and show_none == True:
                calpha = alpha
            elif r == 0 and show_none == False:
                calpha = 0
            else:
                calpha = alpha

            colorStops.append(
                {
                    'ratio' : ratios[idx],
                    'color' : _cmap2rgb(colors=colors[0],
                                        step=cstep,
                                        alpha=calpha)
                }
            )
            r += ratio
            del cstep
        renderer = {
            'type' : 'heatmap',
            'field' : field,
            'blurRadius' : symbol_args.pop('blur_radius', 10),
            "maxPixelIntensity" : maxPixelIntensity,
            "minPixelIntensity" : minPixelIntensity,
            "colorStops" : colorStops
        }
        return renderer
    elif render_type in ['u', 'p'] and\
         "field1" in symbol_args:
        if sdf_or_series is None:
            raise ValueError("sdf_or_series must be a Pandas' Series, SpatialDataFrame" + \
                             " or Pandas DataFrame for this type of renderer")
        st = symbol_args.pop('symbol_type', None)
        ss = symbol_args.pop('symbol_style', None)
        default_symbol = symbol_args.pop('default_symbol', None)
        if default_symbol is None:
            if isinstance(colors, (list, tuple)):
                ccmap = colors[0]
            else:
                ccmap = colors
            default_symbol = create_symbol(
                geometry_type=geometry_type.lower(),
                symbol_type=st,
                symbol_style=ss,
                colors=ccmap,
                **symbol_args)
        field1 = symbol_args.pop("field1", None)
        if field1 is None:
            raise ValueError("You must provide a single field name to use unique value renderer as field1='columnname'")
        field2 = symbol_args.pop('field2', None)
        field3 = symbol_args.pop('field2', None)
        fields = [field1]
        if field2 is not None:
            fields.append(field2)
        if field3 is not None:
            fields.append(field3)
        if isinstance(fields, str):
            fields = [fields]
        field_delimiter = symbol_args.pop('field_delimiter', ',')
        rotation_expression = symbol_args.pop("rotation_expression", None)
        rotation_type = symbol_args.pop("rotation_type", "arithmetic")

        renderer = {
            "type" : "uniqueValue",
            "defaultLabel" : symbol_args.pop('default_label', "Other"),
            "defaultSymbol" : default_symbol,
            "fieldDelimiter" : field_delimiter,
            "rotationExpression" : rotation_expression,
            "rotationType" : rotation_type,
            "valueExpression" : symbol_args.pop("arcade_expression", None),
            "valueExpressionTitle" : symbol_args.pop("arcade_title", None),
            "visualVariables" : symbol_args.pop('visual_variables', vv)
        }
        unique_values = symbol_args.pop('unique_values', None)
        if unique_values is None:
            c = 1
            for f in fields:
                renderer['field%s' % c] = f
                c += 1
            if len(fields) == 1:
                uvals = sdf_or_series[fields[0]].unique().tolist()
            else:
                uvals = sdf_or_series.groupby(fields).size().reset_index().rename(
                    columns={0:'count'}).drop(columns='count').tolist()
                uvals2 = []
                for r in uvals:
                    row = []
                    for i in r:
                        row.append(str(i))
                    uvals2.append(",".join(row))
                    del r
                uvals = uvals2
                if len(uvals) > 255:
                    uvals = uvals[:255]
            unique_values = []
            for idx, uval in enumerate(uvals):
                unique_values.append({
                    "value" : uval,
                    "label" : uval,
                    "description" : "",
                    "symbol" : create_symbol(
                        geometry_type=geometry_type.lower(),
                        symbol_type=st,
                        symbol_style=ss,
                        colors=_get_list_value(idx, colors),
                        **symbol_args)
                })
            renderer['uniqueValueInfos'] = unique_values
        else:
            renderer['uniqueValueInfos'] = unique_values
    elif render_type in ['u-a', 'u'] and \
         "field1" not in symbol_args and\
         'arcade_expression' in symbol_args:
        if sdf_or_series is None:
            raise ValueError("sdf_or_series must be a Pandas' Series, SpatialDataFrame" + \
                             " or Pandas DataFrame for this type of renderer")
        st = symbol_args.pop('symbol_type', None)
        ss = symbol_args.pop('symbol_style', None)
        default_symbol = symbol_args.pop('default_symbol', None)
        if default_symbol is None:
            if isinstance(colors, (list, tuple)):
                ccmap = colors[0]
            else:
                ccmap = colors
            default_symbol = create_symbol(
                geometry_type=geometry_type.lower(),
                symbol_type=st,
                symbol_style=ss,
                colors=ccmap,
                **symbol_args)
        field_delimiter = symbol_args.pop('field_delimiter', ',')
        rotation_expression = symbol_args.pop("rotation_expression", None)
        rotation_type = symbol_args.pop("rotation_type", "arithmetic")

        renderer = {
            "type" : "uniqueValue",
            "defaultLabel" : symbol_args.pop('default_label', "Other"),
            "defaultSymbol" : default_symbol,
            "fieldDelimiter" : field_delimiter,
            "rotationExpression" : rotation_expression,
            "rotationType" : rotation_type,
            "valueExpression" : symbol_args.pop("arcade_expression", None),
            "valueExpressionTitle" : symbol_args.pop("arcade_title", None),
            "visualVariables" : symbol_args.pop('visual_variables', vv)
        }
        if 'unique_values' not in symbol_args:
            raise ValueError("unique_values must be provided if field1 is not given.")
        renderer['uniqueValueInfos'] = symbol_args.pop("unique_values")
    elif render_type == "v":
        renderer = {
            'type' : 'vectorField',
            'visualVariables' : symbol_args.pop('visual_variables', vv),
            'style' : symbol_args.pop('style'),
            'rotationType' : symbol_args.pop('rotation_type', 'arithmetic'),
            'flowRepresentation' : symbol_args.pop('flow', 'flow_from'),
            'attributeField' : symbol_args.pop('attribute_field', None)
        }
    elif render_type == "c":
        if sdf_or_series is None:
            raise ValueError("sdf_or_series must be a Pandas' Series, SpatialDataFrame" + \
                             " or Pandas DataFrame for this type of renderer")
        class_count = symbol_args.pop('class_count', 3) # number of classess for class break
        if len(colors) > 1:
            default_color = colors[1]
            color = colors[0]
        else:
            default_color = colors[0]
            color = colors[0]
        try:
            if isinstance(sdf_or_series, SpatialDataFrame) or \
               hasattr(sdf_or_series, 'geometry_type'):
                gt = sdf_or_series.geometry_type
            elif hasattr(sdf_or_series, 'spatial') and \
                 sdf_or_series.spatial.name and \
                 hasattr(sdf_or_series.spatial, 'geometry_type'):
                gt = sdf_or_series.spatial.geometry_type[0]
        except:
            raise Exception("geometry_type not found, please ensure DataFrame is spatially enabled.")
        renderer = {
            "type" : "classBreaks",
            "valueExpression" : symbol_args.pop('arcade_expression', None),
            'valueExpressionTitle' : symbol_args.pop('arcade_title', None),
            'visualVariables' : symbol_args.pop('visual_variables', vv),
            'rotationType' : symbol_args.pop('rotation_type', 'arithmetic'),
            'rotationExpression' : symbol_args.pop('rotation_expression', None),
            'normalizationType' : symbol_args.pop('normalization_type', None),
            'normalizationTotal' : symbol_args.pop('normalization_total', None),
            'normalizationField' : symbol_args.pop('normalization_field', None),
            'minValue' : symbol_args.pop('min_value', 0),
            'field' : symbol_args.pop('field'),
            'defaultSymbol' : symbol_args.pop('default_symbol', create_symbol(
                geometry_type=gt,
                colors=default_color)
                                              ),
            'defaultLabel' : symbol_args.pop('default_label', 'Other'),
            'classificationMethod' : symbol_args.pop('method', None),
            'classBreakInfos' : [],
            'backgroundFillSymbol' : symbol_args.pop('background_fill_symbol', None)
        }
        minValue = sdf_or_series[renderer['field']].min()
        maxValue = sdf_or_series[renderer['field']].max()
        def pairwise(iterable, fillvalue=999):
            "s -> (s0,s1), (s1,s2), (s2, s3), ..."
            import itertools
            a, b = itertools.tee(iterable)
            next(b, fillvalue)
            return itertools.zip_longest(a, b)
        # calculate the class breaks from column data
        cbs = []
        breaks = np.linspace(minValue, maxValue,
                             num=class_count).tolist()
        steps = np.linspace(0, 255,
                            len(breaks),
                            dtype=np.int)
        ss = symbol_args.pop('symbol_style', None)
        st = symbol_args.pop('symbol_type', None)
        import sys
        for idx, pair in enumerate(pairwise(breaks, fillvalue=sys.maxsize)):
            gt = None
            if isinstance(sdf_or_series, SpatialDataFrame) or \
               hasattr(sdf_or_series, 'geometry_type'):
                gt = sdf_or_series.geometry_type
            elif hasattr(sdf_or_series, 'spatial') and \
                 sdf_or_series.spatial.name and \
                 hasattr(sdf_or_series.spatial, 'geometry_type'):
                gt = sdf_or_series.spatial.geometry_type[0]
            cbs.append({

                'classMaxValue' : pair[1] or pair[0],
                'label' : "%s - %s" % (pair[0], pair[1] or pair[0]),
                'description' : "%s - %s" % (pair[0], pair[1] or pair[0]),
                'symbol' : create_symbol(geometry_type=gt,
                                         symbol_style=ss,
                                         symbol_type=st,
                                         colors=color,
                                         cstep=steps[idx],
                                         **symbol_args)

            })
            del pair
        renderer['classBreakInfos'] = cbs
        for key in [k for k,v in renderer.items() if v is None]:
            del renderer[key]
        return renderer
    elif render_type == "str":
        renderer = {
            'computeGamma' : symbol_args.pop('compute_gamma', True),
            'dra' : symbol_args.pop('dra', None),
            'gamma' : symbol_args.pop('gamma', None),
            'max' : symbol_args.pop('max_value', None),
            'min' : symbol_args.pop('min_value', None),
            'maxPercent' : symbol_args.pop('max_percent', None),
            'minPercent' : symbol_args.pop('min_percent', None),
            "numberOfStandardDeviations" : symbol_args.pop('std', None),
            'sigmoidStrengthLevel' : symbol_args.pop('sigmoid', None),
            'statistics' : symbol_args.pop('statistics', None),
            'stretchType' : symbol_args.pop('type', 'none'),
            'type' : 'rasterStretch',
            'useGamma' : symbol_args.pop('use_gamme', False)
        }
        return renderer
    elif render_type == "t":
        renderer = {
            'type' : 'temporal',
            'latestObservationRenderer' : symbol_args.pop('latest_observation',
                                                          generate_renderer(
                                                              geometry_type="point",
                                                              label="Latest",
                                                              render_type='s',
                                                              colors=colors,
                                                              sdf_or_series=sdf_or_series,
                                                              **symbol_args)
                                                          ),
            'observationRenderer' : symbol_args.pop('observation',
                                                    generate_renderer(
                                                        geometry_type="point",
                                                        label="Observation",
                                                        render_type='s',
                                                        colors=colors,
                                                        sdf_or_series=sdf_or_series,
                                                        **symbol_args)
                                                    ),
            'trackRenderer' : symbol_args.pop('track',
                                              generate_renderer(
                                                  geometry_type="polyline",
                                                  label="Track",
                                                  render_type='s',
                                                  colors=colors,
                                                  sdf_or_series=sdf_or_series,
                                                  **symbol_args)
                                              )
        }
        return renderer
    return renderer
