import datetime
import calendar
import collections
import timesheet.parser

DAYLY_SOLL_TIME = datetime.timedelta(hours=7, minutes=48)
PAUSE_THRESHOLD = datetime.timedelta(hours=5, minutes=0)
LAUNCH_PAUSE = datetime.timedelta(minutes=45)
PADDING = 36 * ' '
BASE = '{0} {1:%d.%m.%Y} '


def print_diff(delta, default_plus=' '):
    sign, time = timesheet.diff_split(delta, default_plus)
    return '{0}{1}'.format(sign, time)


def calc_total(work):
    def diff_time_span(time_span):
        start = datetime.datetime.combine(work.date, time_span.start)
        end = datetime.datetime.combine(work.date, time_span.end)
        return end - start

    def calc_pause(a, b):
        b_start = datetime.datetime.combine(work.date, b.start)
        a_end = datetime.datetime.combine(work.date, a.end)
        return b_start - a_end

    total_time = sum(map(diff_time_span, work.time_spans), timesheet.ZERO_DT)
    double_iterator = ((x, y) for x, y in zip(work.time_spans, work.time_spans[1:]))
    pause = sum((calc_pause(*x) for x in double_iterator), timesheet.ZERO_DT)

    if total_time > PAUSE_THRESHOLD and pause < LAUNCH_PAUSE:
        pause = LAUNCH_PAUSE

    return total_time, pause


class V(object):
    def __init__(self, default=None):
        self.default = default


class NodeMeta(type):

    @classmethod
    def __prepare__(mcs, _name, _bases):
        return collections.OrderedDict()

    def __new__(mcs, clsname, bases, dct):
        attr = {}
        value_set = set()
        arg_list = []
        for name, val in dct.items():
            if isinstance(val, V):
                value_set.add(name)
                attr[name] = val.default
                arg_list.append(name)
            else:
                attr[name] = val
        attr["_value_set"] = value_set
        attr["_arg_list"] = arg_list
        return type.__new__(mcs, clsname, bases, attr)


class Node(object, metaclass=NodeMeta):
    def __init__(self, *args, **kwargs):
        for k, v in zip(range(len(args)), args):
            setattr(self, self._arg_list[k], v)
        for k, v in kwargs.items():
            setattr(self, k, v)


class Special(Node):
    date = V()
    comment = V()
    weekday = V()

    def check(self):
        return True

    def calc(self):
        return self

    def pprint(self):
        fmt_str = BASE + '{2:^20} |'
        return fmt_str.format(timesheet.weekday_str(self.weekday),
                              self.date,
                              self.comment)


class TimeSpan(Node):
    start = V()
    end = V()
    pause = V(datetime.timedelta(0))

    def pprint(self):
        return '{:%H:%M} -- {:%H:%M}'.format(self.start, self.end)


class Work(Node):
    date = V()
    weekday = V()
    total = V()
    saldo = V()
    time_spans = V()
    pause = V(datetime.timedelta(0))

    def check(self):
        total, pause = calc_total(self)
        return (total == self.total and
                self.weekday == self.date.weekday())

    def calc(self):
        new_total, new_pause = calc_total(self)
        new_saldo = new_total - DAYLY_SOLL_TIME - LAUNCH_PAUSE