#!/usr/bin/env python3

# depends on:
# python 3.4 (included in Raspbian Jessie)
# APScheduler


import argparse
import os
from datetime import datetime
import toml

import shepherd.scheduler
import shepherd.config
import shepherd.plugin
import shepherd.control


# Future implementations of checking config differences should be done on
# the hash of the nested conf dict, so comments shouldn't affect this.

# save old config to somewhere in the shepherd root dir - probably need to
# implement a TOML writer in the config module.

# later on, there's going to be an issue with a new config being applied
# remotely, then the system restarting, and an old edit in /boot being
# applied over the top...
# Fix this by saving the working config to /boot when new config applied
# remotely.

def define_core_config(confdef):
    confdef.add_def("id", shepherd.config.StringDef())
    confdef.add_def("plugins", shepherd.config.StringArrayDef())
    confdef.add_def("plugin_dir", shepherd.config.StringDef())
    confdef.add_def("root_dir", shepherd.config.StringDef())
    confdef.add_def("conf_edit_path", shepherd.config.StringDef())

def load_config(config_path,load_editconf):
    # Load config from default location
    confman = shepherd.config.ConfigManager()
    confman.load(os.path.expanduser(config_path))
    # Create core confdef and populate it
    core_confdef = shepherd.config.ConfDefinition()
    define_core_config(core_confdef)
    # attempt to retrive core config and validate it
    core_conf = confman.get_config("shepherd", core_confdef)
    edit_confman = None
    conf_edit_message = None

    if load_editconf:
        # Check for an edit_conf file, and try to load it and plugin configs
        try:
            edit_confman = shepherd.config.ConfigManager()
            edit_confman.load(os.path.expanduser(core_conf["conf_edit_path"]))
            core_edit_conf = edit_confman.get_config("shepherd", core_confdef)

            plugin_classes = shepherd.plugin.find_plugins(
                core_edit_conf["plugins"])
            plugin_configs = edit_confman.get_plugin_configs(plugin_classes)

        except FileNotFoundError:
            conf_edit_message = None
        except shepherd.config.InvalidConfigError as e:
            conf_edit_message = "Invalid config.\n " + str(e.args)
        except toml.TomlDecodeError as e:
            conf_edit_message = "TOML syntax error.\n" + str(e)
        except Exception:
            conf_edit_message = "Error processing new config"
        else:
            conf_edit_message = ("Successfully applied this config at:" +
                                str(datetime.now()))
            confman = edit_confman
            core_conf = core_edit_conf

        if conf_edit_message is not None:
            shepherd.config.update_toml_message(
                os.path.expanduser(core_conf["conf_edit_path"]), conf_edit_message)

    # if editconf failed, load current config for plugins
    if confman is not edit_confman:
        plugin_classes = shepherd.plugin.find_plugins(core_conf["plugins"])
        plugin_configs = confman.get_plugin_configs(plugin_classes)

    # If no editconf file was found, write out the current config as a template
    if (conf_edit_message is None) and load_editconf:
        confman.dump_to_file(os.path.expanduser(core_conf["conf_edit_path"]),
                             "Config generated at:" + str(datetime.now()))

    return (core_conf, plugin_classes, plugin_configs)





def main():
    argparser = argparse.ArgumentParser(description="Keep track of a mob "
                                                    "of roaming Pis")
    argparser.add_argument("configfile", nargs='?', metavar="configfile",
                           help="Path to configfile", default="shepherd.toml")
    argparser.add_argument('-e', '--noedit',help="Disable the editable config temporarily", action="store_true", default=False)
    argparser.add_argument("-t", "--test", help="Test and interface function of the from 'plugin:function'",
                    default=None)

    args = argparser.parse_args()

    confman = shepherd.config.ConfigManager()
    confman.load(os.path.expanduser(args.configfile))

    (core_conf, plugin_classes, plugin_configs) = load_config(args.configfile, not args.noedit)

    if args.test is None:
        shepherd.control.init_control(core_conf)

    shepherd.scheduler.init_scheduler(core_conf)

    shepherd.plugin.init_plugins(plugin_classes, plugin_configs, core_conf)
    
    shepherd.scheduler.restore_jobs()
 
    print(str(datetime.now()))

    if args.test is not None:
        (test_plugin, test_func) = args.test.split(':')
        func = getattr(shepherd.plugin.plugin_functions[test_plugin], test_func)
        print(func())
        return

    print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C'))

    try:
        shepherd.scheduler.start()
    except (KeyboardInterrupt, SystemExit):
        pass


if __name__ == "__main__":
    main()
