# -*- coding: utf-8 -*-
from django.conf import settings
from django.db import models as djangomodels
from django.utils import translation
from django.utils.functional import lazy
from django.utils import six
from .models import OdooModel

"""
    Translation methods
"""


def _get_details_in_lang(field, lang):
    if field.translation_cache.get(lang):
        return field.translation_cache[lang]
    else:
        settings.odoo_models[field.details['model']].cache_translation(lang)
        try:
            return field.translation_cache[lang]
        except:
            return field.details


def field_translate(field, key):
    lang = translation.get_language()
    details = _get_details_in_lang(field, lang)
    res = details.get(key, "")
    if isinstance(res, six.binary_type):
        res = six.text_type(res)
    return res

_ = lazy(field_translate, six.text_type)


def selection_translate(field):
    def trans(val):
        lang = translation.get_language()
        details = _get_details_in_lang(field, lang)
        return dict(details['selection'])[val]

    trans_lazy = lazy(trans, six.text_type)

    res = []
    for val, _label in field.details.get('selection'):
        res.append((val, trans_lazy(val)))
    return tuple(res)


# TODO: valeurs par défaut!
# TODO: domaines

FIELDS_CONV = {
    "char": "CharField",
    "boolean": "BooleanField",
    "integer": "IntegerField",
    "text": "TextField",
    "float": "DecimalField",
    "date": "DateField",
    "datetime": "DateTimeField",
    "time": "TimeField",
    "binary": "BinaryField",
    "selection": "CharField",
    "many2one": "ForeignKey",
    "one2many": None,
    #     "many2many": "",
    #     "function": "",
    #     "related": "",
}


class OdooField(object):

    def __init__(self, details):
        self.details = details
        self.django_field = False
        self.translation_cache = {}  # translations cache
        return super(OdooField, self).__init__()

    def to_django(self, **kwargs):
        kwargs.update({
            "verbose_name": _(self, 'string'),
            "help_text": _(self, 'help'),
            "blank": not(self.details.get("required")),
            "editable": not(self.details.get("readonly")),
        })
        django_field = getattr(djangomodels, FIELDS_CONV[self.details["type"]])(**kwargs)
        django_field.odoo_field = self
        self.django_field = django_field
        return django_field

    def convert_data(self, data):
        return data or None

    def convert_back(self, data):
        return data or False


class TextField(OdooField):

    def to_django(self, **kwargs):
        if self.details.get("required"):
            kwargs["default"] = ""
        kwargs["null"] = not(self.details.get("required"))
        return super(TextField, self).to_django(**kwargs)


class CharField(TextField):

    def to_django(self, **kwargs):
        kwargs['max_length'] = self.details.get('size') or 512
        return super(CharField, self).to_django(**kwargs)


class BooleanField(OdooField):

    def to_django(self, **kwargs):
        kwargs["default"] = False
        return super(BooleanField, self).to_django(**kwargs)

    def convert_data(self, data):
        return data or False


class IntegerField(OdooField):

    def to_django(self, **kwargs):
        if self.details.get("required"):
            kwargs["default"] = 0
        kwargs["null"] = not(self.details.get("required"))
        return super(IntegerField, self).to_django(**kwargs)


class FloatField(IntegerField):

    def to_django(self, **kwargs):
        if self.details.get("digits"):
            kwargs["max_digits"] = self.details["digits"][0]
            kwargs["decimal_places"] = self.details["digits"][1]
        kwargs["null"] = not(self.details.get("required"))
        return super(FloatField, self).to_django(**kwargs)


class DateField(OdooField):

    def to_django(self, **kwargs):
        kwargs["null"] = not(self.details.get("required"))
        if self.details.get("required"):
            kwargs["auto_now_add"] = True
        return super(DateField, self).to_django(**kwargs)


class DateTimeField(DateField):
    pass


class TimeField(DateField):
    pass


class BinaryField(OdooField):

    def to_django(self, **kwargs):
        kwargs["null"] = not(self.details.get("required"))
        return super(BinaryField, self).to_django(**kwargs)


class SelectionField(CharField):

    def to_django(self, **kwargs):
        kwargs["choices"] = selection_translate(self)
        return super(SelectionField, self).to_django(**kwargs)


class Many2OneField(OdooField):

    """
        Si l'objet désigné par details['relation'] existe au sein de django, alors on peut créer le champ directement
        Sinon, on retarde la création du champ au moment de l'éventuelle création de cet objet
    """

    def __new__(cls, details):
        if details['relation'] in settings.odoo_models:
            return OdooField.__new__(cls)
        else:
            settings.deferred_m2o[details['relation']] = settings.deferred_m2o.get(details['relation'], [])
            settings.deferred_m2o[details['relation']].append(details)
            return None

    def to_django(self, **kwargs):
        kwargs["null"] = not(self.details.get("required"))
        if self.details['relation'] == self.details['model']:
            kwargs["to"] = "self"
        else:
            to_model = settings.odoo_models[self.details['relation']]
            kwargs["to"] = to_model
        kwargs["related_name"] = ("%(model)s_%(name)s" % (self.details)).replace('.', '_')
        return super(Many2OneField, self).to_django(**kwargs)

    def convert_data(self, data):
        """
            La data Odoo est une paire (id, valeur textuelle)
            On cherche dans les objets du modèle cible une instance ayant un odoo_id égal au premier
            élément de la paire ; s'il n'existe pas on le load depuis Odoo

            :param (tuple ou False) data: la valeur du champ à convertir
            :return (OdooModel ou False): l'instance d'objet liée via ce champ m2o
        """
        if data and isinstance(data, (list, tuple)) and len(data) == 2:
            to_model = settings.odoo_models[self.details['relation']]
            targets = to_model.objects.filter(odoo_id=data[0])
            if targets:
                return targets[0]
            else:
                return to_model.odoo_load([data[0]])[0]
        return data or None

    def convert_back(self, data):
        """
            La data Django est soit None soit un objet Django
            On le transforme soit en 'False', soit en integer en récupérant l'odoo_id de l'objet cible

            :param (OdooModel ou False) data: la valeur du champ à convertir
            :return (integer ou False): l'id de l'objet cible dans Odoo
        """
        if data and isinstance(data, OdooModel) and hasattr(data, 'odoo_id'):
            return data.odoo_id
#         elif isinstance(data, (int, long)):
#             return data
        else:
            return False


class One2ManyField(OdooField):

    """
        On ne crée pas de champ one2many à proprement parler, on modifie
        simplement l'attribut "relation_field" au champs many2one inverse
        pour qu'il porte le nom de ce champ-ci
    """
    def __new__(cls, details):
        if details['relation'] in settings.odoo_models:
            relation = settings.odoo_models[details['relation']]
            for field in relation._meta.Fields:
                if field.name == details['relation_field']:
                    field.related_name = details['name']
        else:
            settings.deferred_o2m[details['relation']] = settings.deferred_o2m.get(details['relation'], [])
            settings.deferred_o2m[details['relation']].append(details)


def convert_field(details):
    if not(details['type'] in FIELDS_CONV):
        return None
    return eval(details["type"].title() + "Field")(details)
