# -*- coding: utf-8 -*-
"""
Helper functions used in views.
"""

import csv
import logging
from datetime import datetime
from functools import wraps
from json import dumps
from threading import Lock
from time import time as timer

from flask import Response
from lxml import etree

from presence_analyzer.main import app

log = logging.getLogger(__name__)  # pylint: disable=invalid-name
TREE = etree.parse(app.config['DATA_XML'])  # pylint: disable=no-member


class cache(object):  # pylint: disable=invalid-name, too-few-public-methods

    """
    Decorator class
    """

    def __init__(self, seconds):
        """
        If there are decorator arguments, the function
        to be decorated is not passed to the constructor!
        """
        self.seconds = seconds
        self.mem = {}
        self.timer_dict = {}
        self.lock = Lock()

    def __call__(self, func):
        """
        If there are decorator arguments, __call__() is only called
        once, as part of the decoration process! You can only give
        it a single argument, which is the function object.
        """

        def wrapped_func(*args):
            """
            Checking object in cache. If is not, add object to cache
            """

            self.lock.acquire()
            try:
                if func.__name__ in self.mem\
                        and self.cache_is_valid(func.__name__):
                    return self.mem.get(func.__name__)
                else:
                    self.mem[func.__name__] = func(*args)
                    return self.mem.get(func.__name__)
            finally:
                self.lock.release()
        return wrapped_func

    def cache_is_valid(self, func_name):
        """
        Checking if cache content is not expired
        """
        if func_name in self.timer_dict\
                and not self.timer_dict.get(func_name) < timer():
            return True
        else:
            self.timer_dict[func_name] = timer() + self.seconds
            return False


def jsonify(function):
    """
    Creates a response with the JSON representation of wrapped function result.
    """
    @wraps(function)
    def inner(*args, **kwargs):
        """
        This docstring will be overridden by @wraps decorator.
        """
        return Response(
            dumps(function(*args, **kwargs)),
            mimetype='application/json'
        )
    return inner


@cache(600)
def get_data():
    """
    Extracts presence data from CSV file and groups it by user_id.

    It creates structure like this:
    data = {
        'user_id': {
            datetime.date(2013, 10, 1): {
                'start': datetime.time(9, 0, 0),
                'end': datetime.time(17, 30, 0),
            },
            datetime.date(2013, 10, 2): {
                'start': datetime.time(8, 30, 0),
                'end': datetime.time(16, 45, 0),
            },
        }
    }
    """
    data = {}
    with open(app.config['DATA_CSV'], 'r') as csvfile:
        presence_reader = csv.reader(csvfile, delimiter=',')
        for i, row in enumerate(presence_reader):
            if len(row) != 4:
                # ignore header and footer lines
                continue

            try:
                user_id = int(row[0])
                date = datetime.strptime(row[1], '%Y-%m-%d').date()
                start = datetime.strptime(row[2], '%H:%M:%S').time()
                end = datetime.strptime(row[3], '%H:%M:%S').time()
            except (ValueError, TypeError):
                log.debug('Problem with line %d: ', i, exc_info=True)

            data.setdefault(user_id, {})[date] = {'start': start, 'end': end}

    return data


def get_users_from_xml():
    """
    Extracts users data from xml file. Returns dict.
    It creates structure like this:
    {'151': {'avatar': '/api/images/users/151', 'name': 'Dawid J.'}}
    """

    users_data = {}
    for element in TREE.iter('user'):
        users_data[element.get('id')] = {
            'avatar': element[0].text, 'name': element[1].text}
    return users_data


def get_users():