try:
    import collections.abc as collections_abc  # only works on python 3.3+
except ImportError:
    import collections as collections_abc

from .utils import DslBase

def SF(name_or_sf, **params):
    # {"script_score": {"script": "_score"}, "filter": {}}
    if isinstance(name_or_sf, collections_abc.Mapping):
        if params:
            raise ValueError('SF() cannot accept parameters when passing in a dict.')
        kwargs = {}
        sf = name_or_sf.copy()
        for k in ScoreFunction._param_defs:
            if k in name_or_sf:
                kwargs[k] = sf.pop(k)

        # not sf, so just filter+weight, which used to be boost factor
        if not sf:
            name = 'boost_factor'
        # {'FUNCTION': {...}}
        elif len(sf) == 1:
            name, params = sf.popitem()
        else:
            raise ValueError('SF() got an unexpected fields in the dictionary: %r' % sf)

        # boost factor special case, see elasticsearch #6343
        if not isinstance(params, collections_abc.Mapping):
            params = {'value': params}

        # mix known params (from _param_defs) and from inside the function
        kwargs.update(params)
        return ScoreFunction.get_dsl_class(name)(**kwargs)

    # ScriptScore(script="_score", filter=Q())
    if isinstance(name_or_sf, ScoreFunction):
        if params:
            raise ValueError('SF() cannot accept parameters when passing in a ScoreFunction object.')
        return name_or_sf

    # "script_score", script="_score", filter=Q()
    return ScoreFunction.get_dsl_class(name_or_sf)(**params)

class ScoreFunction(DslBase):
    _type_name = 'score_function'
    _type_shortcut = staticmethod(SF)
    _param_defs = {
        'query': {'type': 'query'},
        'filter': {'type': 'query'},
        'weight': {}
    }
    name = None

    def to_dict(self):
        d = super(ScoreFunction, self).to_dict()
        # filter and query dicts should be at the same level as us
        for k in self._param_defs:
            if k in d[self.name]:
                d[k] = d[self.name].pop(k)
        return d

class ScriptScore(ScoreFunction):
    name = 'script_score'

class BoostFactor(ScoreFunction):
    name = 'boost_factor'

    def to_dict(self):
        d = super(BoostFactor, self).to_dict()
        if 'value' in d[self.name]:
            d[self.name] = d[self.name].pop('value')
        else:
            del d[self.name]
        return d

class RandomScore(ScoreFunction):
    name = 'random_score'

class FieldValueFactor(ScoreFunction):
    name = 'field_value_factor'

class Linear(ScoreFunction):
    name = 'linear'

class Gauss(ScoreFunction):
    name = 'gauss'

class Exp(ScoreFunction):
    name = 'exp'

