'''

@author: olivier.massot, 2018
'''
from datetime import datetime
import locale
import re

import cerberus

from core import gis, gis_


def is_french_date(field, value, error):
    try:
        datetime.strptime(value, '%d/%m/%Y')
    except:
        error(field, 'Doit être une date au format jj/mm/aaaa: {}'.format(value))

def is_modern_french_date(field, value, error):
    try:
        d = datetime.strptime(value, '%d/%m/%Y')
        if not d.year >= 2000:
            error(field, "La date ne peut pas être antérieure à l'an 2000: {}".format(value))
    except:
        error(field, 'Doit être une date au format jj/mm/aaaa: {}'.format(value))


def is_int(field, value, error):
    try:
        if  float(value) != int(value):
            error(field, 'Doit être un nombre entier: {}'.format(value))
    except (TypeError, ValueError):
        error(field, 'Doit être un nombre entier: {}'.format(value))

def is_multi_int(field, value, error):
    
    if not re.match(r"\d(\s?-\s?\d)*", str(value)):
        error(field, 'Doit être un nombre entier, ou une liste de nombres séparés par des tirets: {}'.format(value))
        
def is_float(field, value, error):
    try:
        value = locale.atof(str(value))
        if  value != float(value):
            error(field, 'Doit être un nombre décimal ({})'.format(value))
    except ValueError:
        error(field, 'Doit être un nombre décimal ({})'.format(value))


# Ref: http://docs.python-cerberus.org/en/stable/api.html#error-codes

class ExtendedValidator(cerberus.validator.Validator):

    def __init__(self, *args, **kwargs):
        super(ExtendedValidator, self).__init__(*args, **kwargs)
        
        # Rends tous les champs requis par défaut, à moins que 'required' ait été défini dans le schéma
        for field in self.schema:
            if not 'required' in self.schema[field]:
                self.schema[field]['required'] = True

    def _validate_contains_any_of(self, iterable, field, value):
        """ Controle que la chaine contient l'une des substring contenue dans l'iterable

        The rule's arguments are validated against this schema:
        {'type': 'list'}
        """
        if not re.search(r".*{}.*".format("|".join(iterable)), value):
            self._error(field, "Doit contenir un de ces éléments: {} ('{}')".format(", ".join(iterable), value))
        
    def _validate_multiallowed(self, allowed, field, value):
        """ Comme 'allowed', mais autorise plusieurs valeurs séparées par un '-'

        The rule's arguments are validated against this schema:
        {'type': 'list'}
        """
        for item in re.split("\s?-\s?", value or ""):
            if not item in allowed:
                if item == '':
                    self._error(field, "Le champs doit être renseigné")
                else:
                    self._error(field, "Valeur non-autorisée: {}".format(item))

class GeoValidator(ExtendedValidator):
    
    def _validate_inside_box(self, bounding_box, field, value):
        """ Contrôle l'inclusion de la bounding box de l'entité dans la box donneé

        The rule's arguments are validated against this schema:
        {'type': 'list'}
        """
#         geom_type, bounding_box = geom
#         
#         if value.geom_type != geom_type:
#             self._error(field, "Le type de géométrie est {} (attendu: {})".format(value.geom_name, gis_.GEOM_NAMES[geom_type]))
    
        xmin, ymin, xmax, ymax = bounding_box
        
        try:
            x1, y1, x2, y2 = value.bounding_box
            
            if any(x < xmin or x > xmax for x in (x1, x2)) or \
               any(y < ymin or y > ymax for y in (y1, y2)):
                self._error(field, "Certaines coordonnées hors de l'emprise autorisée")
        except AttributeError:
            pass


def _translate_messages(message):
    message = message.replace("string", "texte")
    message = message.replace("integer", "nombre entier")
    message = message.replace("float", "nombre décimal")
    message = message.replace("boolean", "booléen")
    return message

class CerberusErrorHandler(cerberus.errors.BasicErrorHandler):
    messages = {0x00: "{0}",

                0x01: "Le document est manquant",
                0x02: "Le champs est obligatoire",
                0x03: "Champs inconnu",
                0x04: "Le champs '{0}' est obligatoire",
                0x05: "Dépends de ces valeurs: {constraint}",
                0x06: "{0} ne doit pas être présent avec '{field}'",

                0x21: "'{0}' is not a document, must be a dict",
                0x22: "Le champs doit être renseigné",
                0x23: "Les valeurs NULL ne sont pas autorisées",
                0x24: "Doit être du type {constraint}: {value}",
                0x25: "Doit être de type 'dictionnaire': {value}",
                0x26: "La longueur de la liste doit être de {constraint}, elle est de {0}",
                0x27: "La longueur minimum du champs est de {constraint}: {value}",
                0x28: "Trop long, la longueur max. du champs est de {constraint}: {value}",

                0x41: "La valeur n'est pas au bon format ('{constraint}')",
                0x42: "La valeur minimum autorisée est {constraint}",
                0x43: "La valeur maximum autorisée est {constraint}",
                0x44: "Valeur non-autorisée: {value}",
                0x45: "Valeurs non-autorisées: {0}",
                0x46: "Valeur non-autorisée: {value}",
                0x47: "Valeurs non-autorisées: {0}",

                0x61: "Le champs '{field}' ne peut pas être converti: {0}",
                0x62: "Le champs '{field}' ne peut pas être renommé: {0}",
                0x63: "Le champs est en lecture seule",
                0x64: "La valeur par défaut du champs '{field}' ne peut pas être appliquée: {0}",

                0x81: "mapping doesn't validate subschema: {0}",
                0x82: "one or more sequence-items don't validate: {0}",
                0x83: "one or more keys of a mapping  don't validate: {0}",
                0x84: "one or more values in a mapping don't validate: {0}",
                0x85: "one or more sequence-items don't validate: {0}",

                0x91: "one or more definitions validate",
                0x92: "none or more than one rule validate",
                0x93: "no definitions validate",
                0x94: "one or more definitions don't validate"
                }

