"""

MIT License

Copyright (c) 2017-2020 Ioan Coman

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

"""

import os
import sys
import string
import json
import bottle

py = sys.version_info
py3k = py >= (3, 0, 0)


def html_redirect(location):
    return '<!-- Redirect to "{0}" -->\n<html><meta http-equiv="refresh" content="0;url={0}"><body>Redirecting...</body></html>'.format(
        location)


def get_props(configFile):
    """
        Load webserver config
    """
    DATAFOLDER = os.getcwd()
    configFilename = os.path.sep.join((DATAFOLDER, configFile))
    if not os.path.isfile(configFilename):
        DATAFOLDER = os.path.sep.join((os.getcwd(), '..'))
        configFilename = os.path.sep.join((DATAFOLDER, configFile))
    ret = _get_props(configFilename, '=')
    ret['DATAFOLDER'] = DATAFOLDER
    return ret


def _get_props(filename, sep, comment=";#"):
    #
    # sep = separator and may be :, =, etc.
    # comment = a list of chars which define starting of comment
    #
    ret = {}
    overWriteProp = True
    # overWriteProp = False #props with same name are grouped into a list
    try:
        accumulator = ''
        flag = False
        with open(filename, "rt") as f:
            while True:
                line = f.readline()
                if not line:
                    # line must have at least '\r\n' (Windows) or '\n' (Unix)
                    break
                line = line.replace('\r', '')
                line = line.replace('\n', '')
                line = line.lstrip()
                if not line:
                    # line is empty
                    continue
                if line[0] in comment:
                    # line starts with a comment
                    continue
                for c in comment:
                    # remove commented section
                    ix = line.find(c)
                    if -1 != ix:
                        line = line[:line.find(c)]
                if line[-1] == '\\':
                    # current line will continue on next line
                    line = line[:-1]
                    if flag:
                        # add to accumulator
                        accumulator = ' '.join((accumulator, line))
                    else:
                        # set
                        accumulator = line
                    flag = True
                    continue
                if flag:
                    # add last part of line to accumulator
                    accumulator = ' '.join((accumulator, line))
                    line = accumulator
                    accumulator = ''
                    flag = False

                # split line to key and value
                ix = line.find(sep)
                if ix > 0:
                    key = line[:ix].rstrip()
                    value = line[ix + 1:].lstrip().rstrip()
                    try:
                        # if value might be a number
                        value = int(value)
                    except:
                        pass
                    if overWriteProp:
                        ret[key] = value
                    else:
                        # append
                        old = ret.get(key)
                        if old:
                            if type(old) is list:
                                # already a list
                                ret[key].append(value)
                            else:
                                ret[key] = [old, value]
                        else:
                            ret[key] = value
    except Exception as ex:
        print(ex)
    return ret


def _make_login_logout(user, icon, loginurl, logouturl):
    if user:
        title = 'Logout {}'.format(user)
        link = logouturl
    else:
        title = 'Login'
        link = loginurl
    if icon:
        icontitle = '<span class="glyphicon glyphicon-{}"></span> {}'.format(
            icon, title)
    else:
        icontitle = title
    return icontitle, link


def make_bootstrap_navbar(url, bs, menufile, loginurl, logouturl, brandurl, brandtitle):
    userfullname = bs.get('userfullname') or bs.get('username')
    if not py3k and userfullname:
        userfullname = userfullname.encode('utf8')
    user_groups = bs.get('groups') or [0]
    navcode = ''
    L = []
    menuitem = []
    with open(menufile, 'rt') as f:
        for line in f.read().split('\n'):
            line = line.replace('\r', '')
            if line.startswith('#'):
                continue
            if not line.lstrip():
                continue
            link, title, icon, groups, description = line.split('|')
            if not groups:
                groups = '[-1]'  # Default is all authenticated users
            groups = json.loads(groups)
            if line[0] in string.whitespace:
                if menuitem:
                    menuitem[5].append(
                        (link.strip(), title, icon, groups, description))
            else:
                if menuitem:
                    L.append(menuitem)
                menuitem = [link, title, icon, groups, description, []]
    if menuitem:
        L.append(menuitem)
    for i in L:
        link, title, icon, groups, description, childs = i
        if icon:
            icontitle = '<span class="glyphicon glyphicon-{}"></span> {}'.format(
                icon, title)
        else:
            icontitle = title
        hasAccess = False
        for gr in user_groups:
            if gr in groups:
                hasAccess = True
                break
        if title == 'loginlogout':
            hasAccess = True
        if not hasAccess:
            continue
        if len(childs) > 0:
            menuchilds = ''
            for ch in childs:
                ch_link, ch_title, ch_icon, ch_groups, ch_description = ch
                hasAccess = False
                for gr in user_groups:
                    if gr in ch_groups:
                        hasAccess = True
                        break
                if title == 'loginlogout':
                    hasAccess = True
                if not hasAccess:
                    continue
                if ch_icon:
                    ch_icontitle = '<span class="glyphicon glyphicon-{}"></span> {}'.format(
                        ch_icon, ch_title)
                else:
                    ch_icontitle = ch_title
                if ch_title == 'loginlogout':
                    ch_icontitle, ch_link = _make_login_logout(
                        userfullname, ch_icon, loginurl, logouturl)
                if ch_description:
                    ch_descr = ' title="{}"'.format(ch_description)
                else:
                    ch_descr = ''
                if ch_link == url:
                    active = ' class="active"'
                else:
                    active = ''
                menuchilds += '\n<li{}><a href="{}"{}>{}</a></li>'.format(
                    active, ch_link, ch_descr, ch_icontitle)
            if link == url:
                active = ' active'
            else:
                active = ''
            navcode += '''
<li class="dropdown{}">
  <a href="#" title="{}" class="dropdown-toggle" data-toggle="dropdown">{}<b class="caret"></b></a>
  <ul class="dropdown-menu">{}</ul>
</li>'''.format(active, description, icontitle, menuchilds)
        else:
            # no childs
            if description:
                descr = ' title="{}"'.format(description)
            else:
                descr = ''
            if title == 'loginlogout':
                icontitle, link = _make_login_logout(
                    userfullname, icon, loginurl, logouturl)
            if link == url:
                active = ' active'
            else:
                active = ''
            navcode += '\n<li{}><a href="{}"{}>{}</a></li>'.format(
                active, link, descr, icontitle)
    if brandurl and brandtitle:
        brand = '<a href="{}" class="navbar-brand">{}</a>'.format(
            brandurl, brandtitle)
    else:
        brand = ''
    ret = '''
<header class="navbar navbar-inverse navbar-fixed-top bs-docs-nav" role="banner">
  <div class="container">
    <div class="navbar-header">
      <button class="navbar-toggle" type="button" data-toggle="collapse" data-target=".bs-navbar-collapse">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
{brand}
    </div>
    <nav class="collapse navbar-collapse bs-navbar-collapse" role="navigation">
      <ul class="nav navbar-nav navbar-right">
      {navcode}
      </ul>
    </nav>
  </div>
</header>
'''.format(**dict(navcode=navcode, brand=brand))
    return ret


def load_config(config_file):
    """
        Load a webserver module config
        Use app from
            https://github.com/icoman/PropertiesEditor_v1
        to generate and edit config files
    """
    config = {}
    if os.path.isfile(config_file):
        with open(config_file, 'rt') as f:
            jsdoc = json.loads(f.read())
            data = jsdoc.get('data', {})
            for x in data:
                ob = data[x]
                name = ob.get('name')
                value = ob.get('value')
                if ob.get('type') in ('mc',):
                    # mc = multiple checks = a list read by ajax post
                    try:
                        _e = eval(str(value or "[]"))
                        if type(_e) is not type([]):
                            _e = [_e]
                        value = [int(x) for x in _e]
                    except:
                        value = []
                config[name] = value
    return config


class SSLWSGIRefServer(bottle.ServerAdapter):
    def run(self, handler):
        from wsgiref.simple_server import make_server, WSGIRequestHandler
        import ssl
        if self.quiet:
            class QuietHandler(WSGIRequestHandler):
                def log_request(*args, **kw):
                    # pylint: disable=E0211
                    pass

            self.options['handler_class'] = QuietHandler
        srv = make_server(self.host, self.port, handler, **self.options)
        srv.socket = ssl.wrap_socket(
            srv.socket,
            certfile='server.pem',  # path to certificate
            server_side=True)
        srv.serve_forever()


class myWaitressServer(bottle.ServerAdapter):
    # Multi-threaded, poweres Pyramid
    def run(self, handler):
        from waitress import serve
        serve(handler, host=self.host, port=self.port, threads=10)


class myPasteServer(bottle.ServerAdapter):
    # Multi-threaded, stable, tried and tested
    def run(self, handler):  # pragma: no cover
        from paste import httpserver
        httpserver.serve(handler, host=self.host, port=str(self.port),
                         **self.options)


class myCherryPyServer(bottle.ServerAdapter):
    # Multi-threaded and very stable
    def run(self, handler):  # pragma: no cover
        import wsgiserver
        self.options['bind_addr'] = (self.host, self.port)
        self.options['wsgi_app'] = handler

        certfile = self.options.get('certfile')
        if certfile:
            del self.options['certfile']
        keyfile = self.options.get('keyfile')
        if keyfile:
            del self.options['keyfile']

        server = wsgiserver.CherryPyWSGIServer(**self.options)
        if certfile:
            server.ssl_certificate = certfile
        if keyfile:
            server.ssl_private_key = keyfile

        try:
            server.start()
        finally:
            server.stop()


class myGeventWebSocketServer(bottle.ServerAdapter):
    # Use this for websocket support
    def run(self, handler):
        from gevent import pywsgi
        from geventwebsocket.handler import WebSocketHandler
        server = pywsgi.WSGIServer(
            (self.host, self.port), handler, handler_class=WebSocketHandler)
        server.serve_forever()
