#!/usr/libexec/platform-python -s
#  -*- coding: utf-8 -*-
# pylint: disable=invalid-name
# *****************************************************************************
# MLZ library of Tango servers
# Copyright (c) 2015-2020 by the authors, see LICENSE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Module authors:
#   Georg Brandl <g.brandl@fz-juelich.de>
#
# *****************************************************************************

"""Check current server configuration."""

import sys
import os
import logging
import argparse
from os import path

# Add import path for inplace usage
sys.path.insert(0, path.abspath(path.join(path.dirname(__file__), '..')))

# pylint: disable=wrong-import-position
from entangle.server.config import read_resfile
from entangle.server import get_global_config
from entangle.lib.loggers import ColoredConsoleHandler
from entangle.lib.util import import_devcls


class Checker(object):
    def __init__(self, log, resdir=None):
        self.main_log = log
        self._errors = 0
        self._resdir = resdir if resdir is not None else get_global_config()[0]

    def _res_files(self):
        return sorted([path.join(self._resdir, entry)
                       for entry in os.listdir(self._resdir)
                       if entry.endswith('.res')])

    def _all_servers(self):
        return [path.basename(entry)[:-4] for entry in self._res_files()]

    def get_resources(self, server):
        resfile = path.join(self._resdir, server + '.res')
        return read_resfile(resfile, tango_format=False)

    def error(self, msg, *args):
        self._errors += 1
        self.log.error(msg, *args)

    def check(self, servers):
        if not servers:
            servers = self._all_servers()

        seen_devs = {}

        for server in servers:
            self.main_log.debug('checking server: %s', server)
            self.log = logging.getLogger(server)

            try:
                _desc, devices = self.get_resources(server)
            except Exception as e:
                self.error('could not read resources: %s', e)
                continue

            for device in devices:
                self.log = logging.getLogger(server + ' -> ' + device)
                self.log.debug('checking device')

                if device in seen_devs:
                    self.error('duplicate device name, also in server "%s"',
                               seen_devs[device])
                seen_devs[device] = server

                props = devices[device]
                try:
                    clsname = props['type']
                    cls = import_devcls(str(clsname))
                except Exception as e:
                    self.error('cannot import device class: %s', e)
                    continue

                for (propname, propinfo) in cls.properties.items():
                    if propname not in props:
                        if propinfo.default is None:
                            self.error('mandatory property "%s" not in config',
                                       propname)
                        continue
                    value = props[propname]
                    try:
                        propinfo.validator(value)
                    except Exception as e:
                        self.error('value %r for property "%s" is not valid: '
                                   '%s', value, propname, e)

                for propname in props:
                    if propname != 'type' and propname not in cls.properties:
                        self.error('property "%s" exists in resfile but not in '
                                   'the device class', propname)

        if self._errors:
            self.main_log.error('%s server(s) checked, total %d error(s) '
                                'reported', len(servers), self._errors)
        else:
            self.main_log.info('%s server(s) checked, no errors reported',
                               len(servers))

        return 1 if self._errors else 0


def parse_args(argv):
    parser = argparse.ArgumentParser(description='Check entangle server '
                                     'configuration')
    parser.add_argument('-v', '--verbose', action='store_true',
                        help='Verbose logging', default=False)
    parser.add_argument('-r', '--resdir', action='store',
                        help='Use a different resource file directory')
    parser.add_argument('server', type=str, nargs='*',
                        help='One or more server names, default all',
                        default=[])
    return parser.parse_args(argv[1:])


def main(argv):
    args = parse_args(argv)

    loglevel = logging.DEBUG if args.verbose else logging.INFO
    logging.basicConfig(level=loglevel)
    root_log = logging.getLogger()
    root_log.removeHandler(root_log.handlers[0])
    root_log.addHandler(ColoredConsoleHandler())
    log = logging.getLogger('checkconfig')

    return Checker(log, args.resdir).check(args.server)


if __name__ == '__main__':
    sys.exit(main(sys.argv))
