# Path: anubis/url.py
# class Boolean:
# 
#     class Type:
#         fields = ()
# 
#         @staticmethod
#         def set_contents(expr, dictionary):
#             expr.contents = dictionary
# 
#     class Expr(Type):
#         fields = ("field", "args")
# 
#         @staticmethod
#         def repr(expr):
#             return "{}: {}".format(repr(expr["field"]), expr["args"])
# 
#         @staticmethod
#         def set_contents(expr, dictionary):
#             expr.contents = dictionary["contents"]
# 
#         @staticmethod
#         def traverse(expr, func):
#             return func(base_expression=expr)
# 
#     class Not(Type):
#         fields = ("expr",)
# 
#         @staticmethod
#         def repr(expr):
#             return "NOT ({})".format(str(expr["expr"]))
# 
#         @staticmethod
#         def set_contents(expr, dictionary):
#             expr.contents = dict(expr=dictionary["contents"])
# 
#         @staticmethod
#         def traverse(expr, func):
#             return func(not_expression=expr["expr"].traverse(func),
#                         inside_type=expr["expr"].type_.__class__)
# 
#     class And(Type):
#         fields = ("left", "right")
# 
#         @staticmethod
#         def repr(expr):
#             return "({}) AND ({})".format(
#                 str(expr["left"]), str(expr["right"]))
# 
#         @staticmethod
#         def traverse(expr, func):
#             return func(and_expression=(expr["left"].traverse(func),
#                                         expr["right"].traverse(func)),
#                         left_type=expr["left"].type_.__class__,
#                         right_type=expr["right"].type_.__class__)
# 
#     class Or(Type):
#         fields = ("left", "right")
# 
#         @staticmethod
#         def repr(expr):
#             return "({}) OR ({})".format(str(expr["left"]), str(expr["right"]))
# 
#         @staticmethod
#         def traverse(expr, func):
#             return func(or_expression=(expr["left"].traverse(func),
#                                        expr["right"].traverse(func)),
#                         left_type=expr["left"].type_.__class__,
#                         right_type=expr["right"].type_.__class__)
# 
#     types = {"BooleanExpr": Expr(),
#              "Not": Not(),
#              "And": And(),
#              "Or": Or()
#             }
# 
#     precedence = [Expr, Not, And, Or]
# 
#     def __init__(self, contents, type_):
#         assert isinstance(type_, self.Type)
# 
#         self.type_ = type_
#         self.type_.set_contents(self, contents)
# 
#     @classmethod
#     def build(cls, dictionary):
#         if "tag" in dictionary.keys():
#             if dictionary["tag"] in cls.types.keys():
#                 return Boolean(dictionary, cls.types[dictionary["tag"]])
#             else:
#                 raise ValueError(dictionary["contents"])
# 
#         return dictionary
# 
#     def keys(self):
#         return list(self.type_.fields) + ["type"]
# 
#     def traverse(self, func):
#         return self.type_.traverse(self, func)
# 
#     def __getitem__(self, key):
#         if key == "type":
#             return self.type_.__class__.__name__
# 
#         return self.contents[key]
# 
#     # def __iter__(self):
#     #     # For dict() conversion
#     #     for key in self.keys() + ["tag"]:
#     #         yield (key, self.contents[key])
# 
#     def __eq__(self, other):
#         return self.contents == other.contents
# 
#     def __str__(self):
#         return self.type_.repr(self)
# 
#     def __repr__(self):
#         return str(self)


from anubis.url import Boolean


class Aggregator:
    def __init__(self):
        pass

    @staticmethod
    def need_parenthesis(outside, inside):
        outside_precedence = Boolean.precedence.index(outside)
        inside_precedence = Boolean.precedence.index(inside)

        return outside_precedence < inside_precedence

    def __call__(self, base_expression=None, not_expression=None,
                 and_expression=None, or_expression=None, inside_type=None,
                 left_type=None, right_type=None):

        if base_expression is not None:
            return self.handle_base_expression(base_expression)
        elif not_expression is not None:
            need_parens = self.need_parenthesis(Boolean.Not, inside_type)
            return self.handle_not_expression(not_expression, need_parens)
        elif and_expression is not None:
            left_expr, right_expr = and_expression
            left_parens = self.need_parenthesis(Boolean.And, left_type)
            right_parens = self.need_parenthesis(Boolean.And, right_type)
            return self.handle_and_expression(left_expr, right_expr,
                                              left_parens, right_parens)
        elif or_expression is not None:
            left_expr, right_expr = or_expression

# Path: anubis/url.py
# class Boolean:
# 
#     class Type:
#         fields = ()
# 
#         @staticmethod
#         def set_contents(expr, dictionary):
#             expr.contents = dictionary
# 
#     class Expr(Type):
#         fields = ("field", "args")
# 
#         @staticmethod
#         def repr(expr):
#             return "{}: {}".format(repr(expr["field"]), expr["args"])
# 
#         @staticmethod
#         def set_contents(expr, dictionary):
#             expr.contents = dictionary["contents"]
# 
#         @staticmethod
#         def traverse(expr, func):
#             return func(base_expression=expr)
# 
#     class Not(Type):
#         fields = ("expr",)
# 
#         @staticmethod
#         def repr(expr):
#             return "NOT ({})".format(str(expr["expr"]))
# 
#         @staticmethod
#         def set_contents(expr, dictionary):
#             expr.contents = dict(expr=dictionary["contents"])
# 
#         @staticmethod
#         def traverse(expr, func):
#             return func(not_expression=expr["expr"].traverse(func),
#                         inside_type=expr["expr"].type_.__class__)
# 
#     class And(Type):
#         fields = ("left", "right")
# 
#         @staticmethod
#         def repr(expr):
#             return "({}) AND ({})".format(
#                 str(expr["left"]), str(expr["right"]))
# 
#         @staticmethod
#         def traverse(expr, func):
#             return func(and_expression=(expr["left"].traverse(func),
#                                         expr["right"].traverse(func)),
#                         left_type=expr["left"].type_.__class__,
#                         right_type=expr["right"].type_.__class__)
# 
#     class Or(Type):
#         fields = ("left", "right")
# 
#         @staticmethod
#         def repr(expr):
#             return "({}) OR ({})".format(str(expr["left"]), str(expr["right"]))
# 
#         @staticmethod
#         def traverse(expr, func):
#             return func(or_expression=(expr["left"].traverse(func),
#                                        expr["right"].traverse(func)),
#                         left_type=expr["left"].type_.__class__,
#                         right_type=expr["right"].type_.__class__)
# 
#     types = {"BooleanExpr": Expr(),
#              "Not": Not(),
#              "And": And(),
#              "Or": Or()
#             }
# 
#     precedence = [Expr, Not, And, Or]
# 
#     def __init__(self, contents, type_):
#         assert isinstance(type_, self.Type)
# 
#         self.type_ = type_
#         self.type_.set_contents(self, contents)
# 
#     @classmethod
#     def build(cls, dictionary):
#         if "tag" in dictionary.keys():
#             if dictionary["tag"] in cls.types.keys():
#                 return Boolean(dictionary, cls.types[dictionary["tag"]])
#             else:
#                 raise ValueError(dictionary["contents"])
# 
#         return dictionary
# 
#     def keys(self):
#         return list(self.type_.fields) + ["type"]
# 
#     def traverse(self, func):
#         return self.type_.traverse(self, func)
# 
#     def __getitem__(self, key):
#         if key == "type":
#             return self.type_.__class__.__name__
# 
#         return self.contents[key]
# 
#     # def __iter__(self):
#     #     # For dict() conversion
#     #     for key in self.keys() + ["tag"]:
#     #         yield (key, self.contents[key])
# 
#     def __eq__(self, other):
#         return self.contents == other.contents
# 
#     def __str__(self):
#         return self.type_.repr(self)
# 
#     def __repr__(self):
#         return str(self)


# Path: anubis/aggregators.py
from anubis.url import Boolean



class Aggregator:
    def __init__(self):
        pass

    @staticmethod
    def need_parenthesis(outside, inside):
        outside_precedence = Boolean.precedence.index(outside)
        inside_precedence = Boolean.precedence.index(inside)

        return outside_precedence < inside_precedence

    def __call__(self, base_expression=None, not_expression=None,
                 and_expression=None, or_expression=None, inside_type=None,
                 left_type=None, right_type=None):

        if base_expression is not None:
            return self.handle_base_expression(base_expression)
        elif not_expression is not None:
            need_parens = self.need_parenthesis(Boolean.Not, inside_type)
            return self.handle_not_expression(not_expression, need_parens)
        elif and_expression is not None:
            left_expr, right_expr = and_expression
            left_parens = self.need_parenthesis(Boolean.And, left_type)
            right_parens = self.need_parenthesis(Boolean.And, right_type)
            return self.handle_and_expression(left_expr, right_expr,
                                              left_parens, right_parens)
        elif or_expression is not None:
            left_expr, right_expr = or_expression

            left_parens = self.need_parenthesis(Boolean.Or, left_type)