#!/usr/bin/env python3
# encoding: utf-8
#
# Copyright (c) 2019 SAP SE or an SAP affiliate company. All rights reserved.
#
# This file is part of ewm-cloud-robotics
# (see https://github.com/SAP/ewm-cloud-robotics).
#
# This file is licensed under the Apache Software License, v. 2 except as noted
# otherwise in the LICENSE file (https://github.com/SAP/ewm-cloud-robotics/blob/master/LICENSE)
#

"""Run the SAP EWM robot configurator."""

import sys
import signal
import traceback
import logging
import time

from robcoewmrobotconfigurator.ewm_robot_sync import EWMRobotSync
from robcoewmrobotconfigurator.robotconfigcontroller import RobotConfigurationController
from robcoewmrobotconfigurator.robco_robot_api import RobCoRobotAPI

_LOGGER = logging.getLogger(__name__)


class MainLoopController:
    """Control the main loop."""

    def __init__(self):
        """Construct."""
        # Shutdown Handler
        self.shutdown = False
        signal.signal(signal.SIGINT, self.exit_gracefully)
        signal.signal(signal.SIGTERM, self.exit_gracefully)
        # Sleep handler
        self.last_time = time.time()

    def exit_gracefully(self, signum, frame):
        """Set shutdown flag on SIGTERM and SIGINT."""
        self.shutdown = True
        _LOGGER.info('Closing application because signal %s received', signum)

    def sleep(self, seconds: float):
        """Sleep maximum n seconds after the last call."""
        timediff = time.time() - self.last_time

        if timediff < seconds:
            time.sleep(seconds-timediff)

        self.last_time = time.time()


def run_robotconfigurator():
    """Run one instance of the robot configurator."""
    # Register handler to control main loop
    loop_control = MainLoopController()

    # Create CR watcher instances
    k8s_rb = RobCoRobotAPI()
    k8s_rc = RobotConfigurationController()

    # Create EWM robot syncer instance
    robotsync = EWMRobotSync(k8s_rc)

    # Register callback functions
    k8s_rb.register_callback('ConfigurationController', ['ADDED'], k8s_rc.robco_robot_cb)
    k8s_rc.register_callback(
        'EWMRobotSync', ['ADDED', 'MODIFIED', 'REPROCESS'], robotsync.robotconfiguration_cb)
    # Start
    k8s_rb.run()
    k8s_rc.run(reprocess=True)

    _LOGGER.info('SAP EWM Robot Configurator started')

    try:
        # Looping while K8S watchers are running
        while loop_control.shutdown is False:
            # Refresh bearer token when using OAuth
            if robotsync.odataconfig.authorization == robotsync.odataconfig.AUTH_OAUTH:
                robotsync.odatahandler.refresh_access_token()
            # Check if K8S CR handler exception occured
            for k, exc in k8s_rb.thread_exceptions.items():
                _LOGGER.error(
                    'Uncovered exception in "%s" thread of RobCoRobotAPI. Raising it in main '
                    'thread', k)
                raise exc
            for k, exc in k8s_rc.thread_exceptions.items():
                _LOGGER.error(
                    'Uncovered exception in "%s" thread of RobotConfigurationController. Raising '
                    'it in main thread', k)
                raise exc
            # Sleep maximum 1.0 second
            loop_control.sleep(1.0)
    except KeyboardInterrupt:
        _LOGGER.info('Keyboard interrupt - terminating')
    except SystemExit:
        _LOGGER.info('System exit - terminating')
    finally:
        # Stop K8S CR watchers
        _LOGGER.info('Stopping K8S CR watchers')
        k8s_rb.stop_watcher()
        k8s_rc.stop_watcher()
        # Shutdown threadpool executor
        robotsync.executor.shutdown()


if __name__ == '__main__':
    # Create root logger if running as main program
    ROOT_LOGGER = logging.getLogger()
    ROOT_LOGGER.setLevel(logging.INFO)

    # Create console handler and set level to info
    CH = logging.StreamHandler()
    CH.setLevel(logging.INFO)

    # Create formatter
    FORMATTER = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

    # Add formatter to ch
    CH.setFormatter(FORMATTER)

    # Add ch to logger
    ROOT_LOGGER.addHandler(CH)
    # Run robot master
    try:
        run_robotconfigurator()
    except Exception:  # pylint: disable=broad-except
        EXC_INFO = sys.exc_info()
        _LOGGER.critical(
            'Unexpected error "%s" - "%s" - TRACEBACK: %s', EXC_INFO[0], EXC_INFO[1],
            traceback.format_exception(*EXC_INFO))
        sys.exit('Application terminated with exception: "{}" - "{}"'.format(
            EXC_INFO[0], EXC_INFO[1]))

[((25, 10, 25, 37), 'logging.getLogger', 'logging.getLogger', ({(25, 28, 25, 36): '__name__'}, {}), '(__name__)', False, 'import logging\n'), ((61, 13, 61, 28), 'robcoewmrobotconfigurator.robco_robot_api.RobCoRobotAPI', 'RobCoRobotAPI', ({}, {}), '()', False, 'from robcoewmrobotconfigurator.robco_robot_api import RobCoRobotAPI\n'), ((62, 13, 62, 43), 'robcoewmrobotconfigurator.robotconfigcontroller.RobotConfigurationController', 'RobotConfigurationController', ({}, {}), '()', False, 'from robcoewmrobotconfigurator.robotconfigcontroller import RobotConfigurationController\n'), ((65, 16, 65, 36), 'robcoewmrobotconfigurator.ewm_robot_sync.EWMRobotSync', 'EWMRobotSync', ({(65, 29, 65, 35): 'k8s_rc'}, {}), '(k8s_rc)', False, 'from robcoewmrobotconfigurator.ewm_robot_sync import EWMRobotSync\n'), ((111, 18, 111, 37), 'logging.getLogger', 'logging.getLogger', ({}, {}), '()', False, 'import logging\n'), ((115, 9, 115, 32), 'logging.StreamHandler', 'logging.StreamHandler', ({}, {}), '()', False, 'import logging\n'), ((119, 16, 119, 89), 'logging.Formatter', 'logging.Formatter', ({(119, 34, 119, 88): '"""%(asctime)s - %(name)s - %(levelname)s - %(message)s"""'}, {}), "('%(asctime)s - %(name)s - %(levelname)s - %(message)s')", False, 'import logging\n'), ((35, 8, 35, 58), 'signal.signal', 'signal.signal', ({(35, 22, 35, 35): 'signal.SIGINT', (35, 37, 35, 57): 'self.exit_gracefully'}, {}), '(signal.SIGINT, self.exit_gracefully)', False, 'import signal\n'), ((36, 8, 36, 59), 'signal.signal', 'signal.signal', ({(36, 22, 36, 36): 'signal.SIGTERM', (36, 38, 36, 58): 'self.exit_gracefully'}, {}), '(signal.SIGTERM, self.exit_gracefully)', False, 'import signal\n'), ((38, 25, 38, 36), 'time.time', 'time.time', ({}, {}), '()', False, 'import time\n'), ((52, 25, 52, 36), 'time.time', 'time.time', ({}, {}), '()', False, 'import time\n'), ((47, 19, 47, 30), 'time.time', 'time.time', ({}, {}), '()', False, 'import time\n'), ((50, 12, 50, 40), 'time.sleep', 'time.sleep', ({(50, 23, 50, 39): '(seconds - timediff)'}, {}), '(seconds - timediff)', False, 'import time\n'), ((130, 19, 130, 33), 'sys.exc_info', 'sys.exc_info', ({}, {}), '()', False, 'import sys\n'), ((133, 12, 133, 49), 'traceback.format_exception', 'traceback.format_exception', ({(133, 39, 133, 48): '*EXC_INFO'}, {}), '(*EXC_INFO)', False, 'import traceback\n')]