# -*- coding: utf-8 -*-
# bijoe - BI dashboard
# Copyright (C) 2015  Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import re
from datetime import date
from dateutil.relativedelta import relativedelta, MO
import isodate


class RelativeDate(date):
    __TEMPLATES = [
        {
            'pattern': u' *cette +année *',
            'truncate': 'year',
        },
        {
            'pattern': u' *l\'année +dernière *',
            'truncate': 'year',
            'timedelta': {'years': -1},
        },
        {
            'pattern': u' *l\'année +prochaine *',
            'truncate': 'year',
            'timedelta': {'years': 1},
        },
        {
            'pattern': ' *les +(?P<years>[1-9][0-9]*)+ +dernières +années*',
            'truncate': 'year',
            'timedelta': {'years': '-years'},
        },
        {
            'pattern': ' *ce +mois *',
            'truncate': 'month',
        },
        {
            'pattern': ' *le +mois +dernier *',
            'truncate': 'month',
            'timedelta': {'months': -1},
        },
        {
            'pattern': ' *les +(?P<months>[1-9][0-9]*)+ +derniers +mois *',
            'truncate': 'month',
            'timedelta': {'months': '-months'},
        },
        {
            'pattern': ' *le +mois +prochain *',
            'truncate': 'month',
            'timedelta': {'months': 1},
        },
        {
            'pattern': ' *les +(?P<months>[1-9][0-9]*) +prochains +mois *',
            'truncate': 'month',
            'timedelta': {'months': '+months'},
        },
        {
            'pattern': ' *cette +semaine *',
            'truncate': 'week',
        },
        {
            'pattern': ' *ce +trimestre *',
            'truncate': 'quarter',
        },
        {
            'pattern': ' *le +dernier +trimestre *',
            'truncate': 'quarter',
            'timedelta': {'months': -3},
        },
        {
            'pattern': ' *les +(?P<quarters>[1-9][0-9]*) +derniers +trimestres *',
            'truncate': 'quarter',
            'timedelta': {'months': '-3*quarters'},
        },
        {
            'pattern': ' *le +prochain +trimestre *',
            'truncate': 'quarter',
            'timedelta': {'months': 3},
        },
        {
            'pattern': ' *les +(?P<quarters>[1-9][0-9]*) +prochains +trimestres *',
            'truncate': 'quarter',
            'timedelta': {'months': '3*quarters'},
        },
        {
            'pattern': ' *maintenant *',
        },
    ]

    def __new__(cls, s, today=None):
        s = s.strip()
        try:
            d = isodate.parse_date(s)
        except isodate.ISO8601Error:
            for template in cls.__TEMPLATES:
                pattern = template['pattern']
                m = re.match(pattern, s)
                if not m:
                    continue
                d = today or date.today()
                if 'truncate' in template:
                    if template['truncate'] == 'year':
                        d = d.replace(month=1, day=1)
                    if template['truncate'] == 'month':
                        d = d.replace(day=1)
                    if template['truncate'] == 'week':
                        d = d + relativedelta(weekday=MO(-1))
                    if template['truncate'] == 'quarter':
                        d = d.replace(day=1)
                        d = d + relativedelta(months=3 - ((d.month - 1) % 3))
                if 'timedelta' in template:
                    timedelta = template['timedelta']
                    kwargs = {}
                    for key, group in timedelta.iteritems():
                        if hasattr(group, 'encode'):  # string like
                            sign = 1
                            if group.startswith('+'):
                                group = group[1:]
                            elif group.startswith('-'):
                                sign = -1
                                group = group[1:]
                            n = re.match('(\d+)\*(\w+)', group)
                            try:
                                if n:
                                    value = int(n.group(1) * m.group(n.group(2)))
                                else:
                                    value = sign * int(m.group(group))
                                kwargs[key] = value
                            except IndexError:
                                raise RuntimeError('invalid template %r' % template)
                        else:
                            kwargs[key] = group
                    d += relativedelta(**kwargs)
                break
            else:
                raise ValueError('invalid string %r' % s)
        self = date.__new__(cls, d.year, d.month, d.day)
        self.__string_repr = s
        return self

    def __repr__(self):
        return self.__string_repr
