#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
from os.path import isdir, join
from os import listdir
import re
from crontab import CronTab
from creole.client import CreoleClient
import yaml

avScanner = "/usr/share/eole/sbin/avscan"
avScannerConfDir = "/etc/eole/avscan.d/"
days = {"lundi": "MON", 
        "mardi": "TUE",
        "mercredi": "WED",
        "jeudi": "THU",
        "vendredi": "FRI",
        "samedi": "SAT",
        "dimanche": "SUN"}
CUSTOM_RE = re.compile(r'^\s*((((\*/[0-9]+)|([0-9]+-[0-9]+/[0-9]+)|([0-9]+-[0-9]+)|([0-9]+(,[0-9]+)*)|(\*))(\s+(((\*/[0-9]+)|([0-9]+-[0-9]+/[0-9]+)|([0-9]+-[0-9]+)|([0-9]+(,[0-9]+)*)|(\*)))){4})|(@(reboot)|(yearly)|(annually)|(monthly)|(weekly)|(daily)|(midnight)|(hourly)))$')


def is_valid_minute(minute):
    if minute in range(60):
        return True
    return False


def is_valid_hour(hour):
    if hour in range(25):
        return True
    return False


def is_valid_wday(day):
    if day in days.values():
        return True
    return False


def is_valid_mday(day):
    if day in range(32):
        return True
    return False


def is_valid_custom(custom):
    if CUSTOM_RE.match(custom):
        return True
    return False


class AvScan(object):
    def __init__(self, mode='folder', path=None, recursive=True):
        self.mode = mode
        if mode == 'folder' and path:
            self.command = '{0} -d {1}'.format(avScanner, path)
        elif mode == 'share':
            self.command = '{0} -s'.format(avScanner)
        else:
            raise Exception('Could not build command from parameters (mode: {}, path: {})'.format(mode, path))
        self.is_recursive = recursive

    def toCronJob(self, crontab):
        raise NotImplemented('*AvScan class must implement toCronJob method')


class HourlyAvScan(AvScan):
    def __init__(self, mode='folder', path=None, recursive=True, minute=None, **kwargs):
        super(HourlyAvScan, self).__init__(mode=mode, path=path, recursive=recursive)
        self.minute = minute

    def is_valid(self):
        return is_valid_minute(self.minute)

    def toCronJob(self, crontab):
        if self.is_valid():
            job = crontab.new(self.command)
            job.minute.on(self.minute)
            crontab.write()
        else:
            raise Exception('Invalid parameters for cron task')


class DailyAvScan(AvScan):
    def __init__(self, mode='folder', path=None, recursive=True, hour=None, minute=None, **kwargs):
        super(DailyAvScan, self).__init__(mode=mode, path=path, recursive=recursive)
        self.hour = hour
        self.minute = minute

    def is_valid(self):
        return is_valid_hour(self.hour) and is_valid_minute(self.minute)

    def toCronJob(self, crontab):
        if self.is_valid():
            job = crontab.new(self.command)
            job.hour.on(self.hour)
            job.minute.on(self.minute)
            crontab.write()
        else:
            raise Exception('Invalid parameters for cron task')


class WeeklyAvScan(AvScan):
    def __init__(self, mode='folder', path=None, recursive=True, day=None, hour=None, minute=None, **kwargs):
        super(WeeklyAvScan, self).__init__(mode=mode, path=path, recursive=recursive)
        self.day = days[day]
        self.hour = hour
        self.minute = minute

    def is_valid(self):
        return is_valid_wday(self.day) and is_valid_hour(self.hour) and is_valid_minute(self.minute)

    def toCronJob(self, crontab):
        if self.is_valid():
            job = crontab.new(self.command)
            job.dow.on(self.day)
            job.hour.on(self.hour)
            job.minute.on(self.minute)
            crontab.write()
        else:
            raise Exception('Invalid parameters for cron task')


class MonthlyAvScan(AvScan):
    def __init__(self, mode='folder', path=None, recursive=True, day=None, hour=None, minute=None, **kwargs):
        super(MonthlyAvScan, self).__init__(mode=mode, path=path, recursive=recursive)
        self.day = day
        self.hour = hour
        self.minute = minute

    def is_valid(self):
        return is_valid_mday(self.day) and is_valid_hour(self.hour) and is_valid_minute(self.minute)

    def toCronJob(self, crontab):
        if self.is_valid():
            job = crontab.new(self.command)
            job.dom.on(self.day)
            job.hour.on(self.hour)
            job.minute.on(self.minute)
            crontab.write()
        else:
            raise Exception('Invalid parameters for cron task')


class CustomAvScan(AvScan):
    def __init__(self, mode='folder', path=None, recursive=True, custom=None, **kwargs):
        super(CustomAvScan, self).__init__(mode=mode, path=path, recursive=recursive)
        self.custom = custom

    def is_valid(self):
        return is_valid_custom(self.custom)

    def toCronJob(self, crontab):
        if self.is_valid():
            job = crontab.new(self.command)
            job.parse(' '.join(self.custom, self.command))
            crontab.write()
        else:
            raise Exception('Invalid parameters for cron task')


AVCLASSES = {
        'heures': HourlyAvScan,
        'jours': DailyAvScan,
        'semaines': WeeklyAvScan,
        'mois': MonthlyAvScan,
        'custom': CustomAvScan,
        }

def die(exitCode, message=None):
    '''
      Exit with an exit code, make it die properly !
    '''
    if message:
        print(message)
    sys.exit(exitCode)


def getTargets():
    # Load targets described in yaml
    if not isdir(avScannerConfDir):
        die(0)
    targetYAMLs = [join(avScannerConfDir, yml) for yml in listdir(avScannerConfDir) if yml.endswith('.yaml')]
    if not targetYAMLs:
        die(0)
    params = []
    for targetYAML in targetYAMLs:
        with open(targetYAML, 'r') as targetDescription:
            params.extend(yaml.load_all(targetDescription))

    targets = []
    for param_set in params:
        try:
            target = AVCLASSES[param_set['frequency']](**param_set)
            targets.append(target)
        except Exception as err:
            print('Cannot configure cron task with parameters {}'.format(param_set))
            print(err)
    return targets

#
# MAIN
#

if __name__ == "__main__":
    targets = getTargets()
    crontab = CronTab(user=True)
    crontab.remove_all(avScanner)
    if targets:
        for tg in targets:
            tg.toCronJob(crontab)
