#!/usr/bin/python2.7

import os
import signal
import sys

from application import log
from application.process import process, ProcessError
from argparse import ArgumentParser

import sipsimple
import sylk


# noinspection PyUnusedLocal
def stop_server(signum, frame):
    sylk_server = SylkServer()
    sylk_server.stop()


# noinspection PyUnusedLocal
def toggle_debugging(signum, frame):
    if log.level.current != log.level.DEBUG:
        log.level.current = log.level.DEBUG
        log.info('Switched logging level to DEBUG')
    else:
        log.info('Switched logging level to {}'.format(ServerConfig.log_level))
        log.level.current = ServerConfig.log_level


# noinspection PyUnusedLocal
def dump_observers(signum, frame):
    from application.notification import NotificationCenter
    from pprint import pprint
    notification_center = NotificationCenter()
    pprint(notification_center.observers)


if __name__ == '__main__':
    name = 'sylk-server'
    fullname = 'SylkServer'

    process.configuration.subdirectory = 'sylkserver'
    process.runtime.subdirectory = 'sylkserver'

    parser = ArgumentParser(usage='%(prog)s [options]')
    parser.add_argument('--version', action='version', version='%(prog)s {}'.format(sylk.__version__))
    parser.add_argument('--systemd', action='store_true', help='run as a systemd simple service and log to journal')
    parser.add_argument('--no-fork', action='store_false', dest='fork', help='run in the foreground and log to the terminal')
    parser.add_argument('--config-dir', dest='config_directory', default=None, help='the configuration directory', metavar='PATH')
    parser.add_argument('--runtime-dir', dest='runtime_directory', default=None, help='the runtime directory ({})'.format(process.runtime.directory), metavar='PATH')
    parser.add_argument('--enable-bonjour', action='store_true', help='enable Bonjour services')
    parser.add_argument('--debug', action='store_true', help='enable verbose logging')
    parser.add_argument('--debug-memory', action='store_true', help='enable memory debugging')

    options = parser.parse_args()

    if options.config_directory is not None:
        process.configuration.local_directory = options.config_directory
    if options.runtime_directory is not None:
        process.runtime.directory = options.runtime_directory

    if options.systemd:
        from systemd.journal import JournalHandler
        log.set_handler(JournalHandler(SYSLOG_IDENTIFIER=name))
        log.capture_output()
    elif options.fork:
        sys.argv[0] = os.path.realpath(sys.argv[0])  # on fork the current directory changes to / resulting in the wrong resources directory if started with a relative path
        try:
            process.daemonize(pidfile='{}.pid'.format(name))
        except ProcessError as e:
            log.fatal('Failed to start {name}: {exception!s}'.format(name=fullname, exception=e))
            sys.exit(1)
        log.use_syslog(name)

    from sylk.resources import Resources
    from sylk.server import SylkServer, ServerConfig

    log.info('Starting {name} {sylk.__version__}, using SIP SIMPLE SDK {sipsimple.__version__}'.format(name=fullname, sylk=sylk, sipsimple=sipsimple))

    configuration = ServerConfig.__cfgtype__(ServerConfig.__cfgfile__)
    if configuration.files:
        log.info('Reading configuration from {}'.format(', '.join(configuration.files)))
    else:
        log.info('Not reading any configuration files (using internal defaults)')

    log.info('Using resources from {}'.format(Resources.directory))

    if options.debug:
        log.level.current = log.level.DEBUG
    if options.debug_memory:
        from application.debug.memory import memory_dump

    process.signals.add_handler(signal.SIGTERM, stop_server)
    process.signals.add_handler(signal.SIGINT, stop_server)
    process.signals.add_handler(signal.SIGUSR1, toggle_debugging)
    process.signals.add_handler(signal.SIGUSR2, dump_observers)

    server = SylkServer()
    try:
        server.run(options)
    except Exception as e:
        log.fatal('Failed to start {name}: {exception!s}'.format(name=fullname, exception=e))
        log.exception()
        sys.exit(1)
    finally:
        if options.debug_memory:
            memory_dump()

    # the run() method returns after the server is stopped

    if server.state == 'stopped':
        log.info('{name} stopped'.format(name=fullname))
        sys.exit(int(server.failed))
    else:
        log.info('Forcefully exiting {name}...'.format(name=fullname))
        # noinspection PyProtectedMember
        os._exit(1)
