#!/usr/bin/python3

'''
psconfig_pscheduler_agent - Cooridnates various test definitions and manages them in
pScheduler

This daemon does the following:
    * Reads test definitions from an include directory
    * Reads remote test definitions and combines with local settings
    * Manages creation, deletion and maintenance all test definitions in pScheduler
'''

import pathlib

import os
parentdir = pathlib.Path(__file__).resolve().parents[1]

from psconfig.pscheduler.agent import Agent
from psconfig.utilities.logging_utils import LoggingUtils
import pyinotify
import argparse
import time
import logging
import logging.config

##########
# set the process name
############################# not straight forward in python?

##########
# Parse command-line options
# Instantiate the parser
parser = argparse.ArgumentParser(description='psconfig_pscheduler_agent')
parser.add_argument('--config', dest='CONFIG_FILE', action='store', required=True, help='configuration file')
parser.add_argument('--logger', dest='LOGGER_CONF', action='store', help='Logger Config', required=False)
parser.add_argument('--disable-inotify', dest='DISABLE_INOTIFY', action='store_true', help='disable inotify for file change detection.')
parser.add_argument('--verbose', dest='DEBUGFLAG', action='store_true', help='Add additional debug output')
############
#parser.add_argument('--help', dest='HELP', action='store', help='HELP')

args = parser.parse_args()

##########
# Now that we've dropped privileges, create the logger. If we do it in reverse
# order, the daemon won't be able to write to the logger.
logf = LoggingUtils()

if not args.LOGGER_CONF:

    output_level = logging.INFO
    if args.DEBUGFLAG:
        output_level = logging.DEBUG
    
    logging.basicConfig(level=output_level, format=logf.logging_format)
    logger = logging.getLogger('psconfig')

else:
    logging.config.fileConfig(args.LOGGER_CONF)
    logger = logging.getLogger("psconfig")

##########
# Create agent
agent = Agent()
agent.debug = args.DEBUGFLAG
if not agent.init(args.CONFIG_FILE):
    raise Exception('Error initializing agent: {}'.format(agent.error))

##########
# Setup Inotify2 on config files and directories
# NOTE: Don't watch requesting agent file because it may not exist
# NOTE: These do not work in well in container or VM shared directories. Use --disable-inotify
notifier = None
if not args.DISABLE_INOTIFY:
    watcher = pyinotify.WatchManager()
    watchMask = pyinotify.IN_MODIFY
    watcher.add_watch(args.CONFIG_FILE, watchMask) ############verify if file is registered
    watchMask2 = pyinotify.IN_CREATE | pyinotify.IN_DELETE | pyinotify.IN_MODIFY
    try:
        watcher.add_watch(agent.include_directory, watchMask2)
        logger.debug(logf.format('Watching include_directory {}.'.format(agent.include_directory)))
    except Exception as e:
        logger.debug(logf.format('Watching include_directory failed with error {}.'.format(agent.include_directory)))
    try:
        watcher.add_watch(agent.archive_directory, watchMask2)
        logger.debug(logf.format('Watching archive_directory {}.'.format(agent.archive_directory)))
    except Exception as e:
        logger.debug(logf.format('Watching archive_directory failed with error {}.'.format(agent.archive_directory)))
    try:
        watcher.add_watch(agent.transform_directory, watchMask2)
        logger.debug(logf.format('Watching transform_directory {}.'.format(agent.transform_directory)))
    except Exception as e:
        logger.debug(logf.format('Watching transform_directory failed with error {}.'.format(agent.transform_directory)))

    #create notifier
    notifier = pyinotify.Notifier(watcher, timeout=1)

##
# Start main program loop
logger.info(logf.format('Starting pSConfig pScheduler agent'))

while(1):
    ###########
    #Initialize start
    start = time.time()

    #############
    #refresh configs
    logf.generate_guid() #generate new log id for run
    agent.logf.guid = logf.guid #pass to agent so guid consistent
    logger.info(logf.format("Running agent..."))
    agent.run()
    logger.info(logf.format("Agent completed running"))

    ###########
    # Sleep until its time to look for file updates or time to refesh
    end = time.time()
    until_next_refresh = agent.check_interval_seconds - (end - start)
    until_next_file_check = agent.check_config_interval_seconds
    if until_next_refresh < until_next_file_check:
        sleep_time = until_next_refresh
    else:
        sleep_time = until_next_file_check
    
    if agent.will_retry_pscheduler():
        logger.info(logf.format("Retrying pscheduler in {} seconds...".format(sleep_time)))
    else:
        logger.info(logf.format("Time until next record refresh is {} seconds".format(until_next_refresh)))
    
    start = end
    while until_next_refresh > 0:
        time.sleep(sleep_time)

        if notifier and notifier.check_events():
            logger.info(logf.format("Configuration file change detected, refreshing records."))
            notifier.read_events()
            notifier.process_events()
            break
        elif agent.will_retry_pscheduler():
            logger.info(logf.format("Retrying pscheduler after failure"))
            break
        else:
            end = time.time()
            until_next_refresh -= end-start
            start = end

#Exit the whole process
os._exit()
