#!/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:
#   Alexander Lenz <alexander.lenz@frm2.tum.de>
#
# *****************************************************************************

"""Dump current server configuration in a RST table format."""

from __future__ import print_function

import sys
import os
import logging
import argparse
from os import path
from collections import OrderedDict

# 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.pycompat import text_type


def srepr(s):
    if isinstance(s, text_type):
        return repr(s)[1:]
    if isinstance(s, list):
        return '[' + ', '.join(srepr(x) for x in s) + ']'
    return repr(s)


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

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

    @property
    def servers(self):
        return [path.basename(entry)[:-4] for entry in self.res_files]

    def get_devices(self, server):
        return sorted(self.get_resources(server))

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

    def print_info(self, outformat):
        data = OrderedDict([
            (entry, self.get_resources(entry)) for entry in sorted(self.servers)
        ])

        srvlen, devlen, resnamelen, resvaluelen = self._calc_maxlengths(data)

        lines = self.formats[outformat](self, data, srvlen, devlen, resnamelen,
                                        resvaluelen)
        print('\n'.join(lines))

    def get_lines_dokuwiki(self, data, _srvlen, devlen, resnamelen, resvaluelen):
        lines = []

        # overall heading
        lines.append('=' * 5 + ' Entangle servers ' + '=' * 5)
        lines.append('')

        for srvname, (srvdesc, res) in data.items():
            # server heading
            lines.append('=' * 4 + ' ' + srvname + ' ' + '=' * 4)
            lines.append('')
            lines.append(srvdesc)
            lines.append('')
            lines.append('')

            header = '^ {:%i} ^ {:%i} ^ {:%i} ^' \
                     % (devlen, resnamelen, resvaluelen)
            devresline = '| {:%i} | {:%i} | {:%i} |' \
                         % (devlen, resnamelen, resvaluelen)
            # device table header
            lines.append(header.format('Device', 'Parameter', 'Value'))

            # device/param table
            for devname, devres in res.items():
                lines.append(devresline.format(devname, '',
                                               devres.get('description',
                                                          '').strip('\'')))

                for param, value in devres.items():
                    if param == 'description':
                        continue
                    # escape double slash if neccessary (e.g. for iodevs)
                    value = srepr(value).replace('//', '%%//%%')
                    lines.append(devresline.format('', param, value))

            lines.append('')
            lines.append('')

        return lines

    def get_lines_rst(self, data, _srvlen, devlen, resnamelen, resvaluelen):
        lines = []

        # overall heading
        heading = 'Entangle servers'
        lines.append('=' * len(heading))
        lines.append(heading)
        lines.append('=' * len(heading))
        lines.append('')

        for srvname, (srvdesc, res) in data.items():
            # server heading
            lines.append(srvname)
            lines.append('=' * len(srvname))
            lines.append('')
            lines.append(srvdesc)
            lines.append('')
            lines.append('')

            # device table header
            lines.append('=' * devlen + ' ' + '=' * resnamelen + ' ' + '='
                         * resvaluelen)

            devresline = '{:%i} {:%i} {:%i}' % (devlen, resnamelen, resvaluelen)
            lines.append(devresline.format('Device', 'Parameter', 'Value'))
            lines.append('=' * devlen + ' ' + '=' * resnamelen + ' ' + '='
                         * resvaluelen)

            # device/param table
            for devname, devres in res.items():
                lines.append(devresline.format(devname, '',
                                               devres.get('description',
                                                          '').strip('\'')))
                for param, value in devres.items():
                    if param == 'description':
                        continue
                    lines.append(devresline.format(
                        '\\', param, srepr(value).replace('\\', '\\\\')))

            # device table footer
            lines.append('=' * devlen + ' ' + '=' * resnamelen + ' ' + '='
                         * resvaluelen)

            lines.append('')
            lines.append('')

        return lines

    def _calc_maxlengths(self, data):
        srvlen = max(len(srvname) for srvname in data)
        devlen = max(len(devname)
                     for srvdata in data.values()
                     for devname in srvdata[1])
        resnamelen = max(len(devname)
                         for srvdata in data.values()
                         for devdata in srvdata[1].values()
                         for devname in devdata)
        resvaluelen = 30
        return srvlen, devlen, resnamelen, resvaluelen

    formats = {
        'rst': get_lines_rst,
        'dokuwiki': get_lines_dokuwiki,
    }


def parse_args(argv):
    parser = argparse.ArgumentParser(description='Print entangle server '
                                     'information')
    parser.add_argument('-v', '--verbose', action='store_true',
                        help='Verbose logging', default=False)
    parser.add_argument('-f', '--format', type=str,
                        choices=list(InfoAggregator.formats),
                        help='Select the output format', default='rst')
    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, format='%(message)s')

    info = InfoAggregator()
    info.print_info(args.format)


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