#!/usr/bin/env python3

import os
import sys
import time
import chalk
import click
import atexit
import importlib
import socketserver

from sensed.Map import Map, ConfigMap
from sensed.SensedServer import SensedServer


def _debug(msg, tag='\\\\\\/'):
    if tag == 'INFO':
        disp = chalk.blue
    elif tag == 'WARN':
        disp = chalk.yellow
    elif tag == 'ERROR':
        disp = chalk.red
    elif tag == 'BANNER':
        disp = chalk.cyan
        tag = '\\\\\\/'
    else:
        disp = chalk.green

    tag = '[{}]'.format(tag).rjust(7)
    msg = '[{}] {} :: {}'.format(time.asctime(), tag, msg)
    disp(msg)


@click.command()
@click.option('--config', '-c', default=None,
              help='Configuration file for this instance.')
@click.option('--name', '-n', default='sensed',
              help='Name of his sensed instance. Default: sensed')
@click.option('--sensors', '-S', default=[],
              help='Sensor modules to load and enable.')
@click.option('--host', '-i', default='localhost',
              help='IP or hostname to bind to. Default: localhost')
@click.option('--port', '-p', default=3000,
              help='Port to bind to. Default: 3000')
@click.option('--test', '-t', is_flag=True,
              help='Enable test mode.')
@click.option('--ci', is_flag=True, help='CI Testing.')
def sensed(config, name, sensors, host, port, test, ci):
    if config is None:
        cfg = ConfigMap()
        cfg.sensed = Map({'name': name, 'host': host,
                          'port': port, 'test': test,
                          'sensors': []})
    else:
        cfg = ConfigMap(filename=config)

        _debug('Loaded config', tag='INFO')

        if 'sensed' not in cfg:
            cfg.sensed = Map({'name': name, 'host': host,
                              'port': port, 'test': test,
                              'sensors': []})
        else:
            cfg.sensed = Map(cfg.sensed)

        if 'sensors' not in cfg.sensed:
            _debug(chalk.yellow, 'no sensors configured, disabling')
            cfg.sensed.sensors = []
        if 'host' not in cfg.sensed:
            _debug(chalk.yellow,
                   'no host configured, defaulting to localhost')
            cfg.sensed.host = 'localhost'
        if 'port' not in cfg.sensed:
            _debug(chalk.yellow,
                   'no port configured, defaulting to 3000')
            cfg.sensed.port = 3000
        if 'name' not in cfg.sensed:
            _debug(chalk.yellow,
                   'no name configured, defaulting to sensed')
            cfg.sensed.name = 'sensed'
        if 'test' not in cfg.sensed:
            cfg.sensed.test = False

    _debug('Initializing sensed server', tag='INFO')
    server = socketserver.UDPServer((cfg.sensed.host, cfg.sensed.port),
                                    SensedServer)

    if len(cfg.sensed.sensors) > 0:
        _debug('Loading modules:', tag='INFO')
        server.sensors = Map()
        for sensor in cfg.sensed.sensors:
            try:
                smod = importlib.import_module(sensor)
                s_key = sensor.replace('.', '-')
                if isinstance(cfg[s_key], dict):
                    cfg[s_key] = Map(cfg[s_key])
                server.sensors[sensor] = smod.Sensor(cfg)
                _debug(' * {}'.format(sensor), tag='INFO')
            except Exception as e:
                _debug(' ! {} - {}: {}'.format(sensor, str(e.__class__), e),
                       tag='ERROR')
                e.with_traceback()

    server.config = cfg

    _debug('sensed v{} ready'.format(SensedServer.__version__), tag='BANNER')
    if cfg.sensed.test == True:
        _debug('test mode is active', tag='WARN')

    if ci is True:
        _debug('testing successful, terminating')
        server.server_close()
        sys.exit(0)
    else:
        @atexit.register
        def close():
            _debug('shutting down')
            server.shutdown()
        server.serve_forever()

if __name__ == '__main__':
    sensed()
