# w.c.s. - web application for online forms
# Copyright (C) 2005-2016  Entr'ouvert
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.

"""
Collection of utility functions to be used in the context of calls to
eval(). They are made available in the "utils" namespace of
get_global_eval_dict.
"""

import datetime
import re
import time

from django.utils.encoding import force_bytes

from wcs.qommon import force_str
from .misc import get_as_datetime

today = datetime.date.today
now = datetime.datetime.now


def make_date(date_var):
    '''Extract a date from a datetime, a date, a struct_time or a string'''
    if isinstance(date_var, datetime.datetime):
        return date_var.date()
    if isinstance(date_var, datetime.date):
        return date_var.replace()  # use replace to un-lazy
    if isinstance(date_var, time.struct_time) or (
            isinstance(date_var, tuple) and len(date_var) == 9):
        return datetime.date(*date_var[:3])
    try:
        return get_as_datetime(str(date_var)).date()
    except ValueError:
        raise ValueError('invalid date value: %s' % date_var)


def make_datetime(datetime_var):
    '''Extract a date from a datetime, a date, a struct_time or a string'''
    if isinstance(datetime_var, datetime.datetime):
        return datetime_var.replace()  # use replace to un-lazy
    if isinstance(datetime_var, datetime.date):
        return datetime.datetime(year=datetime_var.year, month=datetime_var.month,
                                 day=datetime_var.day)
    if isinstance(datetime_var, time.struct_time) or (
            isinstance(datetime_var, tuple) and len(datetime_var) == 9):
        return datetime.datetime(*datetime_var[:6])
    try:
        return get_as_datetime(str(datetime_var))
    except ValueError:
        raise ValueError('invalid datetime value: %s' % datetime_var)

def date(var, month=None, day=None):
    if var and month and day:
        return datetime.date(int(var), int(month), int(day))
    return make_date(var)

def days(count):
    return datetime.timedelta(days=int(count))

def time_delta(t1, t2):
    return make_date(t1) - make_date(t2)

def date_delta(t1, t2):
    '''Return the timedelta between two date like values'''
    t1, t2 = make_date(t1), make_date(t2)
    return t1 - t2


def datetime_delta(t1, t2):
    '''Return the timedelta between two datetime like values'''
    t1, t2 = make_datetime(t1), make_datetime(t2)
    return t1 - t2


def age_in_years_and_months(born, today=None):
    '''Compute age since today as the number of years and months elapsed'''
    born = make_date(born)
    if today is None:
        today = datetime.date.today()
    today = make_date(today)
    before = (today.month, today.day) < (born.month, born.day)
    years = today.year - born.year
    months = today.month - born.month
    if before:
        years -= 1
        months += 12
    if today.day < born.day:
        months -= 1
    return years, months


def age_in_years(born, today=None):
    '''Compute age since today as the number of years elapsed'''
    return age_in_years_and_months(born, today=today)[0]


def age_in_days(born, today=None):
    '''Compute age since today as the number of days elapsed'''
    born = make_date(born)
    if today is None:
        today = datetime.date.today()
    today = make_date(today)
    return date_delta(today, born).days


def age_in_seconds(born, today=None):
    '''Compute age since today as the number of seconds elapsed'''
    born = make_datetime(born)
    if today is None:
        today = datetime.datetime.now()
    today = make_datetime(today)
    return datetime_delta(today, born).total_seconds()


def add_days(date, count):
    '''Add the given number of days to date'''
    return make_date(date) + datetime.timedelta(days=count)


def attachment(content, filename='', content_type='application/octet-stream'):
    '''Serialize content as an attachment'''
    import base64

    return {
        'filename': filename,
        'content_type': content_type,
        'b64_content': force_str(base64.b64encode(force_bytes(content))),
    }


def dict_from_prefix(prefix, in_dict):
    '''Return a dict based on a dict filtered by a key prefix.

       The prefix is removed from the key.

       Intent: meant to help build a PicklableUpload from a set
               of key/values stored in the workflow data.

       Note: to use this function in a context of a Python
             expression, you should pass the _wf_data_ using
             the function locals()

       Example: utils.dict_from_prefix('akey_', locals())
         Where: the workflow data contains the key/values:
                akey_filename     = <filename>
                akey_content_type = <mime_type>
                akey_b64_content  = <content base64 encoded>
           And: it produces a dict like the key/values are:
                filename     = wf_data['akey_filename']
                content_type = wf_data['akey_content_type']
                b64_content  = wf_data['akey_b64_content']
    '''
    return {k[len(prefix):]: v for k, v in in_dict.items() if k.startswith('%s' % prefix)}
