from six import add_metaclass

from .utils import DslMeta, DslBase, BoolMixin

class FilterMeta(DslMeta):
    _classes = {}

def F(name_or_filter, **params):
    # {"term": {...}}
    if isinstance(name_or_filter, dict):
        if params:
            raise ValueError('F() cannot accept parameters when passing in a dict.')
        if len(name_or_filter) != 1:
            raise ValueError('F() can only accept dict with a single filter ({"bool": {...}}). '
                 'Instead it got (%r)' % name_or_filter)
        name, params = name_or_filter.copy().popitem()
        return Filter.get_dsl_class(name)(**params)

    # Term(...)
    if isinstance(name_or_filter, Filter):
        if params:
            raise ValueError('F() cannot accept parameters when passing in a Filter object.')
        return name_or_filter

    # 'term', tag='python', ...
    return Filter.get_dsl_class(name_or_filter)(**params)

@add_metaclass(FilterMeta)
class Filter(DslBase):
    _type_name = 'filter'
    _type_shortcut = staticmethod(F)
    name = None

class Term(Filter):
    name = 'term'

class Terms(Filter):
    name = 'terms'

class Range(Filter):
    name = 'range'

class Missing(Filter):
    name = 'missing'

class Bool(BoolMixin, Filter):
    name = 'bool'
    _param_defs = {
        'must': {'type': 'filter', 'multi': True},
        'should': {'type': 'filter', 'multi': True},
        'must_not': {'type': 'filter', 'multi': True},
    }

# register this as Bool for Filter
Filter._bool = Bool

class MatchAll(Filter):
    name = 'match_all'
    def __add__(self, other):
        return other._clone()
    __and__ = __rand__ = __radd__ = __add__

    def __or__(self, other):
        return self
    __ror__ = __or__

EMPTY_FILTER = MatchAll()

@@ -70,16 +70,33 @@
 oolMixin
+, _make_dsl_class
 %0A%0Aclass 
@@ -161,18 +161,124 @@
 er, 
-**params):
+filters=None, **params):%0A    # 'and/or', %5BF(), F()%5D%0A    if filters is not None:%0A        params%5B'filters'%5D = filters%0A
 %0A   
@@ -1170,20 +1170,24 @@
 %0A%0Aclass 
-Term
+MatchAll
 (Filter)
@@ -1204,115 +1204,148 @@
  = '
-term'%0A%0Aclass Terms(Filter):%0A    name = 'terms'%0A%0Aclass Range(Filter):%0A    name = 'range'%0A%0Aclass Missing(Filt
+match_all'%0A    def __add__(self, other):%0A        return other._clone()%0A    __and__ = __rand__ = __radd__ = __add__%0A%0A    def __or__(self, oth
 er):
@@ -1353,24 +1353,70 @@
     
+   
-name = 'missing'
+ return self%0A    __ror__ = __or__%0AEMPTY_FILTER = MatchAll()
 %0A%0Acl
@@ -1647,17 +1647,16 @@
 ,%0A    %7D%0A
-%0A
 # regist
@@ -1705,32 +1705,27 @@
 Bool%0A%0Aclass 
-MatchAll
+Not
 (Filter):%0A  
@@ -1730,34 +1730,78 @@
     name = '
-match_all'
+not'%0A    _param_defs = %7B'filter': %7B'type': 'filter'%7D%7D%0A
 %0A    def __a
@@ -1791,35 +1791,36 @@
 r'%7D%7D%0A%0A    def __
-add
+init
 __(self, other):
@@ -1816,184 +1816,1134 @@
 lf, 
-other):%0A        return other._clone()%0A    __and__ = __rand__ = __radd__ = __add__%0A%0A    def __or__(self, other):%0A        return self%0A    __ror__ = __or__%0A%0AEMPTY_FILTER = MatchAll()
+**kwargs):%0A        if 'filter' not in kwargs:%0A            kwargs = %7B'filter': kwargs%7D%0A        super(Not, self).__init__(**kwargs)%0A%0AFILTERS = (%0A    # compound filters%0A    ('and', %7B'filters': %7B'type': 'filter', 'multi': True%7D%7D),%0A    ('or', %7B'filters': %7B'type': 'filter', 'multi': True%7D%7D),%0A%0A    # relationships%0A    ('nested', %7B'filter': %7B'type': 'filter'%7D%7D),%0A    ('has_child', %7B'filter': %7B'type': 'filter'%7D%7D),%0A    ('has_parent', %7B'filter': %7B'type': 'filter'%7D%7D),%0A%0A    ('query', %7B'query': %7B'type': 'query'%7D%7D),%0A%0A    # core filters%0A    ('exists', None),%0A    ('geo_bounding_box', None),%0A    ('geo_distance', None),%0A    ('geo_distance_range', None),%0A    ('geo_polygon', None),%0A    ('geo_shape', None),%0A    ('geohash_cell', None),%0A    ('ids', None),%0A    ('indices', None),%0A    ('limit', None),%0A    ('missing', None),%0A    ('prefix', None),%0A    ('range', None),%0A    ('regexp', None),%0A    ('script', None),%0A    ('term', None),%0A    ('terms', None),%0A    ('type', None),%0A)%0A%0A# generate the filter classes dynamicaly%0Afor fname, params_def in FILTERS:%0A    fclass = _make_dsl_class(Filter, fname, params_def)%0A    globals()%5Bfclass.__name__%5D = fclass%0A
 %0A

Make filter generation dynamic as well, include all the filters