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

__all__ = ('dateprop', 'datesyn',
           'timeprop', 'timesyn',
           'stimeprop', 'stimesyn',
           'weekdayprop', 'weekdaysyn',
           )

from sqlalchemy.sql import operators
from sqlalchemy.orm import synonym
from sqlalchemy.orm.properties import ColumnProperty
from ulib.base.times import Time
from ulib.formats import dateF, timeF, weekdayF

def _as_date(date):
    date = dateF.parse(date)
    if date is not None: date = date.date
    return date

def dateprop(name='_date'):
    u"""Dans un modèle, créer une propriété qui contient des instances de
    Date, e.g:

        class MyModel(Model):
            date =  dateprop('_date')

            @classmethod
            def __map(cls, mapper, tables):
                mymodels = tables.mymodels
                properties = dict(date=datesyn('_date'))
                return mapper(MyModel, mymodels, properties=properties), mymodels

    Notez qu'il faut définir le synonyme dans le mapping pour que les recherches
    sur cette propriété fonctionnent.
    """
    def get_date(self): return dateF.parse(getattr(self, name))
    def set_date(self, date): setattr(self, name, _as_date(date))
    return property(get_date, set_date)

class DateComparator(ColumnProperty.Comparator):
    def __lt__(self, other): return self.operate(operators.lt, _as_date(other))
    def __le__(self, other): return self.operate(operators.le, _as_date(other))
    def __eq__(self, other): return self.operate(operators.eq, _as_date(other))
    def __ne__(self, other): return self.operate(operators.ne, _as_date(other))
    def __gt__(self, other): return self.operate(operators.gt, _as_date(other))
    def __ge__(self, other): return self.operate(operators.ge, _as_date(other))
    def in_(self, other): return self.operate(operators.in_op, map(_as_date, other))
    def __add__(self, other): return self.operate(operators.add, _as_date(other))
    def __sub__(self, other): return self.operate(operators.sub, _as_date(other))
    def __radd__(self, other): return self.reverse_operate(operators.add, _as_date(other))
    def __rsub__(self, other): return self.reverse_operate(operators.sub, _as_date(other))

def datesyn(name='_date'):
    return synonym(name, map_column=True, comparator_factory=DateComparator)

def _as_time(time):
    time = timeF.parse(time)
    if time is not None: time = time.time
    return time

def timeprop(name='_time'):
    u"""Dans un modèle, créer une propriété qui contient des instances de
    Time, e.g:

        class MyModel(Model):
            time =  timeprop('_time')

            @classmethod
            def __map(cls, mapper, tables):
                mymodels = tables.mymodels
                properties = dict(time=timesyn('_time'))
                return mapper(MyModel, mymodels, properties=properties), mymodels

    Notez qu'il faut définir le synonyme dans le mapping pour que les recherches
    sur cette propriété fonctionnent.
    """
    def get_time(self): return timeF.parse(getattr(self, name))
    def set_time(self, time): setattr(self, name, _as_time(time))
    return property(get_time, set_time)

class TimeComparator(ColumnProperty.Comparator):
    def __lt__(self, other): return self.operate(operators.lt, _as_time(other))
    def __le__(self, other): return self.operate(operators.le, _as_time(other))
    def __eq__(self, other): return self.operate(operators.eq, _as_time(other))
    def __ne__(self, other): return self.operate(operators.ne, _as_time(other))
    def __gt__(self, other): return self.operate(operators.gt, _as_time(other))
    def __ge__(self, other): return self.operate(operators.ge, _as_time(other))
    def in_(self, other): return self.operate(operators.in_op, map(_as_time, other))
    def __add__(self, other): return self.operate(operators.add, _as_time(other))
    def __sub__(self, other): return self.operate(operators.sub, _as_time(other))
    def __radd__(self, other): return self.reverse_operate(operators.add, _as_time(other))
    def __rsub__(self, other): return self.reverse_operate(operators.sub, _as_time(other))

def timesyn(name='_time'):
    return synonym(name, map_column=True, comparator_factory=DateComparator)

def _as_stime(time):
    time = timeF.parse(time)
    if time is not None: time = time.nbsecs
    return time

def stimeprop(name='_time'):
    u"""Dans un modèle, créer une propriété qui contient des instances de
    Time, stockée comme un nombre entier de secondes depuis minuit, e.g:

        class MyModel(Model):
            time =  stimeprop('_time')

            @classmethod
            def __map(cls, mapper, tables):
                mymodels = tables.mymodels
                properties = dict(time=stimesyn('_time'))
                return mapper(MyModel, mymodels, properties=properties), mymodels

    Notez qu'il faut définir le synonyme dans le mapping pour que les recherches
    sur cette propriété fonctionnent.
    """
    def get_stime(self):
        stime = getattr(self, name)
        if stime is not None: return Time(s=stime)
        else: return None
    def set_stime(self, time):
        setattr(self, name, _as_stime(time))
    return property(get_stime, set_stime)

class STimeComparator(ColumnProperty.Comparator):
    def __lt__(self, other): return self.operate(operators.lt, _as_stime(other))
    def __le__(self, other): return self.operate(operators.le, _as_stime(other))
    def __eq__(self, other): return self.operate(operators.eq, _as_stime(other))
    def __ne__(self, other): return self.operate(operators.ne, _as_stime(other))
    def __gt__(self, other): return self.operate(operators.gt, _as_stime(other))
    def __ge__(self, other): return self.operate(operators.ge, _as_stime(other))
    def in_(self, other): return self.operate(operators.in_op, map(_as_stime, other))
    def __add__(self, other): return self.operate(operators.add, _as_stime(other))
    def __sub__(self, other): return self.operate(operators.sub, _as_stime(other))
    def __radd__(self, other): return self.reverse_operate(operators.add, _as_stime(other))
    def __rsub__(self, other): return self.reverse_operate(operators.sub, _as_stime(other))

def stimesyn(name='_time'):
    return synonym(name, map_column=True, comparator_factory=STimeComparator)

def _as_weekday(weekday):
    return weekdayF.get_weekday(weekday)

def weekdayprop(name='_weekday'):
    u"""Dans un modèle, créer une propriété qui contient un jour de la semaine,
    de 'Lundi' à 'Dimanche', stocké comme un nombre entier de 0 à 6, e.g:

        class MyModel(Model):
            weekday =  weekdayprop('_weekday')

            @classmethod
            def __map(cls, mapper, tables):
                mymodels = tables.mymodels
                properties = dict(weekday=weekdaysyn('_weekday'))
                return mapper(MyModel, mymodels, properties=properties), mymodels

    Notez qu'il faut définir le synonyme dans le mapping pour que les recherches
    sur cette propriété fonctionnent.
    """
    def get_weekday(self):
        weekday = getattr(self, name)
        if weekday is not None: return weekdayF.parse(weekday)
        else: return None
    def set_weekday(self, weekday):
        setattr(self, name, _as_weekday(weekday))
    return property(get_weekday, set_weekday)

class WeekdayComparator(ColumnProperty.Comparator):
    def __lt__(self, other): return self.operate(operators.lt, _as_weekday(other))
    def __le__(self, other): return self.operate(operators.le, _as_weekday(other))
    def __eq__(self, other): return self.operate(operators.eq, _as_weekday(other))
    def __ne__(self, other): return self.operate(operators.ne, _as_weekday(other))
    def __gt__(self, other): return self.operate(operators.gt, _as_weekday(other))
    def __ge__(self, other): return self.operate(operators.ge, _as_weekday(other))
    def in_(self, other): return self.operate(operators.in_op, map(_as_weekday, other))
    def __add__(self, other): return self.operate(operators.add, _as_weekday(other))
    def __sub__(self, other): return self.operate(operators.sub, _as_weekday(other))
    def __radd__(self, other): return self.reverse_operate(operators.add, _as_weekday(other))
    def __rsub__(self, other): return self.reverse_operate(operators.sub, _as_weekday(other))

def weekdaysyn(name='_weekday'):
    return synonym(name, map_column=True, comparator_factory=WeekdayComparator)
