#!/usr/bin/env python3

import sys, os, time, datetime, argparse, pprint, asyncio, threading, glob
from crontab import CronTab
from yapsy.PluginManager import PluginManager
from pylioshelpers.logging import logging

log = logging.log

CRON_CHECK_INTERVAL = 60
_loop = {}

pprint = pprint.PrettyPrinter(indent=4).pprint

parser = argparse.ArgumentParser(description="Run with tasks in python")
args = parser.parse_args()


def main():
    print()
    log("Loading plugins ...", "info")
    # Load the plugins from the plugins directory
    manager = PluginManager()
    manager.setPluginInfoExtension("plugin")
    plugin_directories = get_plugin_directories(
        os.path.dirname(os.path.realpath(__file__)) + "/plugins"
    )
    manager.setPluginPlaces(plugin_directories)
    manager.collectPlugins()

    plugins = manager.getAllPlugins()
    log("Plugins loaded:", "info")
    for plugin in plugins:
        log("- " + plugin.name, "info")

    print()

    log("Starting event loop")
    while True:
        try:
            for plugin in plugins:
                task_time = CronTab(plugin.plugin_object.get_schedule())
                task_until = round(task_time.next(None, default_utc=True))

                if task_until < CRON_CHECK_INTERVAL:
                    log(
                        "Plugin: {plugin_name} - Executing".format(
                            plugin_name=plugin.name
                        )
                    )
                    fire_and_forget(plugin.name, plugin.plugin_object.perform())
                else:
                    if task_until < (CRON_CHECK_INTERVAL * 6):
                        log(
                            "Plugin: {plugin_name} - Sleeping - Next event {next_event}s - Schedule: {crontab}".format(
                                plugin_name=plugin.name,
                                next_event=task_until,
                                crontab=plugin.plugin_object.get_schedule(),
                            ),
                            "info",
                        )

            sys.stdout.flush()
            time.sleep(CRON_CHECK_INTERVAL)

        except (KeyboardInterrupt, SystemExit):
            log("KeyboardInterrupt received. Exiting...", "warn")
            print()
            exit()


def fire_and_forget(plugin_name: str, coro):
    global _loop
    if plugin_name not in _loop.keys():
        _loop[plugin_name] = asyncio.new_event_loop()
        threading.Thread(target=_loop[plugin_name].run_forever, daemon=True).start()
    _loop[plugin_name].call_soon_threadsafe(asyncio.create_task, coro)


def get_plugin_directories(base_dir: str):
    plugin_directories = []
    directories = glob.glob(base_dir + "/*/")
    for directory in directories:
        if "__pycache__" not in directory:
            plugin_directories.append(directory)
    return plugin_directories


if __name__ == "__main__":
    main()
