#!python


"""autodock-cron

This plugin runs containers on a regular scheduled as defined by
the environment variable ``CRON`` of the form ``m h d mon dow``
much like crond on many Linux/UNIX systems.

The container must first be run at least once for ``autodock-cron``
to pick up the new container and its configuration.

Note that ``autodock-cron`` makes use of the Docker API and effectively
calls ``docker start`` on your container; a new container is **NOT** created
on very run of the schedule.
"""


from __future__ import print_function

import re
import sys
import logging
from time import gmtime, time
from operator import itemgetter


from cronex import CronExpression

from autodock.plugin import Plugin

from circuits import handler, BaseComponent, Event, Timer


logging.basicConfig(
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    level=logging.INFO,
    stream=sys.stderr
)


logger = logging.getLogger(__name__)


ENV_REGEX = re.compile("[=]*")


def parse_args(parser):
    return parser


class check(Event):
    """check Event"""


class Scheduler(BaseComponent):

    def init(self, rpc):
        self.rpc = rpc

        self.jobs = {}

        Timer(60, check(), persist=True).register(self)

    def add(self, id, cron):
        self.jobs[id] = CronExpression("{} {}".format(cron, id[:10]))

    def delete(self, id):
        del self.jobs[id]

    @handler("registered")
    def _on_registered(self, component, manager):
        if component is self:
            logging.info("Scheduler registered and ready!")

    @handler("check")
    def _on_check(self):
        for id, job in self.jobs.items():
            if job.check_trigger(gmtime(time())[:5]):
                logging.info("Scheduler triggered start for {}".format(job))
                self.rpc.docker("start", id)

    def __contains__(self, id):
        return id in self.jobs


class Cron(Plugin):

    def init(self, *args):
        super(Cron, self).init(*args)

        self.scheduler = Scheduler(self.rpc).register(self)

        map(self._add_container, map(itemgetter("Id"), self.rpc.docker("containers", True, True)))

    def _add_container(self, id):
        if id in self.scheduler:
            return

        cron = self._get_container_info(id)

        if not cron:
            return

        logging.info("Container {} requested cron scheduling".format(id[:10]))

        self.scheduler.add(id, cron)

    def _get_container_info(self, id):
        info = self.rpc.docker("inspect_container", id)

        env = dict(map(ENV_REGEX.split, info["Config"]["Env"] or []))

        cron = env.get("CRON", "")

        return cron

    def _remove_container(self, id):
        if id not in self.scheduler:
            return

        logging.info("Container {} scheduling removed".format(id[:10]))

        self.scheduler.delete(id)

    def container_created(self, event, **data):
        logging.info("Container {} created".format(data["id"][:10]))

        self._add_container(data["id"])

    def container_destroyed(self, event, **data):
        logging.info("Container {} destroyed".format(data["id"][:10]))

        self._remove_container(data["id"])


def main():
    Cron(parse_args).run()


if __name__ == "__main__":
    main()
