# coding=utf-8
""" Some util functions """

import pandas as pd
from copy import deepcopy
import ee
import re


def getReducerName(reducer):
    """
    Get the name of the parsed reducer.

    WARNING: This function makes a request to EE Servers (getInfo). Do not use
    in server side functions (example: inside a mapping function)
    """
    reducer_type = reducer.getInfo()['type']

    relations = dict(
        mean=['Reducer.mean', 'Reducer.intervalMean'],
        median=['Reducer.median'],
        mode=['Reducer.mode'],
        first=['Reducer.first', 'Reducer.firstNonNull'],
        last=['Reducer.last', 'Reducer.lastNonNull'],
        stdDev=['Reducer.stdDev', 'Reducer.sampleStdDev'],
        all=['Reducer.allNoneZero'],
        any=['Reducer.anyNoneZero'],
        count=['Reducer.count', 'Reducer.countDistinct'],
        max=['Reducer.max'],
        min=['Reducer.min'],
        product=['Reducer.product'],
        variance=['Reducer.sampleVariance', 'Reducer.variance'],
        skew=['Reducer.skew'],
        sum=['Reducer.sum']
    )

    for name, options in relations.items():
        if reducer_type in options:
            return name


def reduceRegionsPandas(data, index='system:index', add_coordinates=False,
                        duplicate_index=False):
    """ Transform data coming from Image.reduceRegions to a pandas dataframe

    :param data: data coming from Image.reduceRegions
    :type data: ee.Dictionary or dict
    :param index: the index of the dataframe
    :param add_coordinates: if True adds the coordinates to the dataframe
    :param duplicate_index: if True adds the index data to the dataframe too
    :return: a pandas dataframe
    :rtype: pd.DataFrame
    """
    if not isinstance(data, dict):
        if add_coordinates:
            def addCentroid(feat):
                feat = ee.Feature(feat)
                centroid = feat.centroid().geometry()
                coords = ee.List(centroid.coordinates())
                return feat.set('longitude', ee.Number(coords.get(0)),
                                'latitude', ee.Number(coords.get(1)))
            data = data.map(addCentroid)

        data = data.getInfo()

    features = data['features']

    d, indexes = [], []
    for feature in features:
        nf = deepcopy(feature)
        props = nf['properties']

        if not duplicate_index:
            props.pop(index) if index in props else props

        d.append(props)
        if index == 'system:index':
            indexes.append(feature['id'])
        else:
            indexes.append(feature['properties'][index])

    return pd.DataFrame(d, indexes)


def castImage(value):
    """ Cast a value into an ee.Image if it is not already """
    if isinstance(value, ee.Image) or value is None:
        return value
    else:
        return ee.Image.constant(value)


def makeName(img, pattern, date_pattern=None):
    """ Make a name with the given pattern. The pattern must contain the
    propeties to replace between curly braces. There are 2 special words:

    * 'system_date': replace with the date of the image formatted with
      `date_pattern`, which defaults to 'yyyyMMdd'
    * 'id' or 'ID': the image id. If None, it'll be replaced with 'id'

    Pattern example (supposing each image has a property called `city`):
    'image from {city} on {system_date}'
    """
    properties = re.findall(r'{(\w+)}', pattern)
    to_replace = {}
    for prop in properties:
        if prop in ['system_date']:
            if date_pattern is None:
                date_pattern = 'yyyyMMdd'
            condition = img.propertyNames().contains('system:time_start')
            value = ee.String(ee.Algorithms.If(condition,
                                               img.date().format(date_pattern),
                                               'date'))
            value = value.getInfo()
        elif prop in ['id', 'ID']:
            value = img.id().getInfo()
            if value is None:
                value = 'id'
        else:
            value = img.get(prop).getInfo()
            if value is None:
                value = prop

        to_replace[prop] = value
    return pattern.format(**to_replace)


def dict2namedtuple(thedict, name='NamedDict'):
    """ Create a namedtuple from a dict object. It handles nested dicts. If
    you want to scape this behaviour the dict must be placed into a list as its
    unique element """
    from collections import namedtuple

    thenametuple = namedtuple(name, [])

    for key, val in thedict.items():
        if not isinstance(key, str):
            msg = 'dict keys must be strings not {}'
            raise ValueError(msg.format(key.__class__))

        if not isinstance(val, dict):
            # workaround to include a dict as an attribute
            if isinstance(val, list):
                if isinstance(val[0], dict):
                    val = val[0]

            setattr(thenametuple, key, val)
        else:
            newname = dict2namedtuple(val, key)
            setattr(thenametuple, key, newname)

    return thenametuple
@@ -5066,20 +5066,519 @@
  return thenametuple
+%0A%0A%0Adef formatVisParams(visParams):%0A    %22%22%22 format visualization parameters to match EE requirements at%0A    ee.data.getMapId %22%22%22%0A    formatted = dict()%0A    for param, value in visParams.items():%0A        if param in %5B'bands', 'palette'%5D:%0A            formatted%5Bparam%5D = ','.join(value) if len(value) == 3 else str(value%5B0%5D)%0A        if param in %5B'min', 'max', 'gain', 'bias', 'gamma'%5D:%0A            formatted%5Bparam%5D = str(value) if isinstance(value, (int, str)) else ','.join(value)%0A    return formatted%0A

format visualization parameters to match ee.data.getMapId