#!/usr/bin/python3 -OO
# -*- encoding: utf-8 -*-
"""
iep-spd
=======
The supplicant-processor daemon.
"""
import logging
import logging.handlers
import os
import signal
import sys
import time
import traceback

import iep_spd.config
from iep_spd.config import SPD_CONFIG
import iep_spd.led
import iep_spd.links

_logger = logging.getLogger('spd')

_ALIVE = True

def _graceful_shutdown():
    global _ALIVE
    if _ALIVE:
        _logger.warning("System shutdown beginning...")
        _ALIVE = False
    else:
        _logger.warning("System shutting down immediately")
        sys.exit(1)
        
def _term_handler(signum, frame):
    _logger.warning("Received SIGTERM")
    _graceful_shutdown()
    
def _set_up_logging(parser_args):
    logging.root.setLevel(logging.DEBUG)
    
    console_logging = SPD_CONFIG.logging_console
    if parser_args.debug:
        console_logging = iep_spd.config.LOGGING_DEBUG
        
    if console_logging == iep_spd.config.LOGGING_DEBUG:
        formatter = logging.Formatter(
            "%(asctime)s : %(levelname)s : %(name)s:%(lineno)d[%(threadName)s] : %(message)s"
        )
    else:
        formatter = logging.Formatter(
            "%(asctime)s : %(levelname)s : %(name)s : %(message)s"
        )
        
    if console_logging:
        if logging.root.handlers:
            _logger.info("Configuring console-based logging...")
        console_logger = logging.StreamHandler()
        console_logger.setLevel(console_logging.upper())
        console_logger.setFormatter(formatter)
        logging.root.addHandler(console_logger)
        _logger.info("Console-based logging online")
        
    if SPD_CONFIG.logging_file: #Disk-based logging is desired
        if logging.root.handlers:
            _logger.info("Configuring file-based logging for " + SPD_CONFIG.logging_file + "...")
        file_logger = logging.handlers.RotatingFileHandler(
            SPD_CONFIG.logging_file,
            maxBytes=SPD_CONFIG.logging_file_max_size,
            backupCount=SPD_CONFIG.logging_file_history_count,
        )
        if logging.root.handlers:
            _logger.info("Configured file-based logging with rotation every %(count)i bytes and history of %(filecount)i" % {
                'count': SPD_CONFIG.logging_file_max_size,
                'filecount': SPD_CONFIG.logging_file_history_count,
            })
        file_logger.setLevel(SPD_CONFIG.logging_level)
        file_logger.setFormatter(formatter)
        logging.root.addHandler(file_logger)
        _logger.info("File-based logging online")
        
if __name__ == '__main__':
    import argparse
    parser = argparse.ArgumentParser(description="Centralised daemon for managing network connections")
    parser.add_argument("--config", help="specify an alternate configuration file", default="/etc/3d-p/iep-spd/iep-spd.ini")
    parser.add_argument("--debug", help="output logging information at the DEBUG level", action="store_true")
    args = parser.parse_args()
    del parser
    
    SPD_CONFIG.read(args.config, or_die=True)
    
    _set_up_logging(args)
    del _set_up_logging
    _logger.warning("=" * 40)
    _logger.warning("=" * 40)
    _logger.warning("System startup in progress...")
    
    signal.signal(signal.SIGTERM, _term_handler)
    _logger.debug("Installed SIGTERM handler")
    
    if not os.path.exists(SPD_CONFIG.system_statefile_path):
        os.makedirs(SPD_CONFIG.system_statefile_path, 0o755)
        
    _logger.info("Enumerating interfaces to monitor...")
    monitored_interfaces = []
    #BEGIN GROSS HACK
    _hack_bridged_ports = set()
    
    def _hack_filter(interfaces, category):
        for i in interfaces:
            if i.name in _hack_bridged_ports:
                _logger.error("{category} interface {interface} is a member of a bridge; this is a configuration conflict".format(
                    category=category,
                    interface=i.name,
                ))
            else:
                yield i
                
    for interface in SPD_CONFIG.bridge_interfaces:
        monitored_interfaces.append(iep_spd.links.BridgeInterface(interface))
        bridge_path = os.path.join('/sys/class/net', interface.name, 'brif')
        if os.path.isdir(bridge_path):
            _hack_bridged_ports.update(os.listdir(bridge_path))
        else:
            _logger.error("Bridge {bridge} does not exist".format(
                bridge=interface.name,
            ))
    for interface in _hack_filter(SPD_CONFIG.rj45_interfaces, 'RJ45'):
        monitored_interfaces.append(iep_spd.links.RJ45Interface(interface))
    for interface in _hack_filter(SPD_CONFIG.wifi_interfaces, 'WiFi'):
        monitored_interfaces.append(iep_spd.links.WiFiInterface(interface))
    for interface in _hack_filter(SPD_CONFIG.cell_interfaces, 'Cell'):
        monitored_interfaces.append(iep_spd.links.CellInterface(interface))
    for interface in _hack_filter(SPD_CONFIG.mea_interfaces, 'MEA'):
        monitored_interfaces.append(iep_spd.links.MEAInterface(interface))
    for interface in _hack_filter(SPD_CONFIG.rajant_interfaces, 'Rajant'):
        monitored_interfaces.append(iep_spd.links.RajantInterface(interface))
        
    del _hack_bridged_ports
    #END GROSS HACK (it is really gross and disgusting you guys like so bad eew)
    
    if monitored_interfaces:
        _logger.info("Starting interface monitors...")
        for interface in monitored_interfaces:
            interface.start()
    else:
        _logger.warning("No configured interfaces to monitor")
        iep_spd.led.report_status(True) #Turn on the LED
        sys.exit(0)
        
    _logger.info("System online; normal operation underway")
    try:
        while _ALIVE:
            time.sleep(1)
    except KeyboardInterrupt:
        _logger.warning("System shutdown requested by keyboard interrupt")
    except Exception:
        _logger.critical("System shutdown by unhandled exception:\n{exc}".format(
            exc=traceback.format_exc(),
        ))
        sys.exit(1)
    else:
        _logger.warning("System shutdown requested by operating system")
        
