# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8

__all__ = (
    'ANY', 'ALL', 'NONE', 'EXISTS',
)

from .utils import *

class Expr(object):
    _terms, terms = None, property(lambda self: self._terms)

    def __init__(self, *terms):
        self._terms = listof(terms or None, None)

    @staticmethod
    def match_dict(dict, object):
        for name, value in dict.items():
            one_match = False
            attr_values = object.get(name, ())
            for value in listof(value):
                if value in attr_values:
                    one_match = True
                    break
            if not one_match: return False
        return True

    @staticmethod
    def match_term(term, object):
        """tester le terme par rapport à l'objet.
        * si c'est None, retourner vrai
        * si c'est un dictionnaire, tous les attributs cités doivent avoir au
          moins une des valeurs fournies
        * si c'est une fonction (plus exactement un objet appelable), elle doit
          prendre l'unique argument (object) et retourner True si l'objet
          correspond
        * si c'est une liste, la traiter comme ANY(*term)
        * si c'est une instance de Expr, déléguer le traitement à sa méthode
          match()
        * sinon, lancer une exception.
        """
        if term is None:
            return True
        elif isinstance(term, dict):
            return Expr.match_dict(term, object)
        elif callable(term):
            return term(object)
        elif isseq(term):
            term = ANY(*term)
        if isinstance(term, Expr):
            return term.match(object)
        else:
            raise ValueError("Argument invalide %r" % term)
        
    def match(self, object):
        return False

class ANY(Expr):
    """construire l'objet avec une liste de termes. au moins un des termes doit
    correspondre
    """
    def match(self, object, lazy=True):
        result = False
        if self.terms is None: return result
        for term in self.terms:
            if self.match_term(term, object):
                result = True
                if lazy: break
        return result

class ALL(Expr):
    """construire l'objet avec une liste de termes. tous les termes doivent
    correspondrent
    """
    def match(self, object, lazy=True):
        result = True
        if self.terms is None: return result
        for term in self.terms:
            if not self.match_term(term, object):
                result = False
                if lazy: break
        return result

class NONE(Expr):
    """construire l'objet avec une liste de termes. aucun des termes ne doit
    correspondre
    """
    def match(self, object, lazy=False):
        result = True
        if self.terms is None: return result
        for term in self.terms:
            if self.match_term(term, object):
                result = False
                if lazy: break
        return result

class EXISTS(Expr):
    """construire l'objet avec une liste d'attributs. tous les attributs doivent
    exister
    """
    def match(self, object, lazy=True):
        result = True
        if self.terms is None: return result
        for term in self.terms:
            if not object.has_key(term):
                result = False
                if lazy: break
        return result
