#!/usr/bin/env python3

from __future__ import print_function

from datetime import datetime
import sys
import argparse

import reflectrpc
from reflectrpc.client import RpcClient

def format_type(typename):
    if not len(typename):
        return typename

    if typename[0].isupper():
        return '<a href="#type-%s">%s</a>' % (typename, typename)

    return typename

parser = argparse.ArgumentParser(description="Generate documentation from a running ReflectRPC service")

parser.add_argument("host", metavar='HOST', type=str, help="Host to connect to")
parser.add_argument("port", metavar='PORT', type=int, help="Port to connect to")
parser.add_argument("outfile", metavar='OUTFILE', type=str, help="Output file")

parser.add_argument('--http', default=False, action='store_true',
        help='Use HTTP as transport protocol')
parser.add_argument('--http-path', default='',
        help='HTTP path to send RPC calls to (default is /rpc)')

parser.add_argument('-t', '--tls', default=False, action='store_true',
        help='Use TLS authentication and encryption on the RPC connection')
parser.add_argument('--check-hostname', default=False, action='store_true',
        help='Check server hostname against its TLS certificate')
parser.add_argument('-C', '--ca', default='',
        help='Certificate Authority to check the server certificate against')
parser.add_argument('-k', '--key', default='',
        help='Key for client authentication')
parser.add_argument('-c', '--cert', default='',
        help='Certificate for client authentication')

args = parser.parse_args()

client = RpcClient(args.host, args.port)

if args.http:
    client.enable_http(args.http_path)

if args.tls:
    client.enable_tls(args.ca, args.check_hostname)

if args.cert and args.key:
    args.client.enable_client_auth(args.cert, args.key)

service_description = ''
functions = []
custom_types = []

try:
    try:
        service_description = client.rpc_call('__describe_service')
    except RpcError:
        pass

    try:
        functions = client.rpc_call('__describe_functions')
    except RpcError:
        pass

    try:
        custom_types = client.rpc_call('__describe_custom_types')
    except RpcError:
        pass
except reflectrpc.client.NetworkError as e:
    print(e, file=sys.stderr)
    print('', file=sys.stderr)
    self.connection_failed_error(True)

f = open(args.outfile, "w")

style = """
<style type="text/css">
body {
    font-family: Verdana, Geneva, sans-serif;
    margin: 3em;
}

a {
    color: #4078c0;
}

a:link {
    text-decoration: none;
}

a:visited {
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}

a:active {
    text-decoration: underline;
}

h1 {
    border-bottom: 1px solid #ddd;
}

h2 {
    border-bottom: 1px solid #ddd;
}

h3 {
    margin-bottom: 0;
}

.description {
    color: #888;
}

.typename {
    font-style:italic;
}

table.withborders {
    border-spacing: 0;
    border-top: 1px solid #ddd;
    border-right: 1px solid #ddd;
}

table.withborders th {
    border-left: 1px solid #ddd;
    border-bottom: 1px solid #ddd;
    padding: 0.5em;
    background-color: #eee;
}

table.withborders td {
    border-left: 1px solid #ddd;
    border-bottom: 1px solid #ddd;
    padding: 0.5em;
}
</style>
"""

title = service_description['name'] + ' - ReflectRPC Service Documentation'

f.write('<!DOCTYPE html>\n')
f.write('<html>\n<head>\n<meta charset="UTF-8">\n')
f.write('<title>%s</title>\n%s</head>\n<body>\n' % (title, style))

f.write('<h1>ReflectRPC Service Documentation</h1>\n')
f.write('<table>')
f.write('<tr><td>Service Name:</td><td>%s</td></tr>\n' % (service_description['name']))
f.write('<tr><td>Version:</td><td>%s</td></tr>\n' % (service_description['version']))
f.write('<tr><td>Description:</td><td>%s</td></tr>\n' % (service_description['description']))
f.write('<tr><td>&nbsp;</td><td>&nbsp;</td></tr>\n')
f.write('<tr><td>Generated from:</td><td>%s:%d</td></tr>\n' % (args.host, args.port))
f.write('<tr><td>Generated at:</td><td>%s (UTC)</td></tr>\n' % (str(datetime.utcnow().replace(microsecond=0))))
f.write('<tr><td>Generated by:</td><td>ReflectRPC (rpcdoc) %s</td></tr>\n' % (reflectrpc.version))
f.write('</table>')

f.write('<h2>Types</h2>\n')
for t in custom_types:
    if t['type'] == 'enum':
        f.write('<a name="type-%s"></a>\n' % (t['name']))
        f.write('<h3>%s (Enum)</h3>\n' % (t['name']))
        f.write('<div class="description">%s</div>\n' % (t['description']))
        f.write('<p>Enum values:</p>')
        f.write('<table class="withborders">')
        f.write('<tr><th>Int value</th><th>String value</th><th>Description</th></tr>')
        for value in t['values']:
            f.write('<tr><td>%d</td><td>%s</td><td>%s</td></tr>\n' % (value['intvalue'], value['name'], value['description']))
        f.write('</table>')
    elif t['type'] == 'hash':
        f.write('<a name="type-%s"></a>\n' % (t['name']))
        f.write('<h3>%s (Named Hash)</h3>\n' % (t['name']))
        f.write('<div class="description">%s</div>\n' % (t['description']))
        f.write('<p>Fields of this type:</p>')
        f.write('<table class="withborders">')
        f.write('<tr><th>Name</th><th>Type</th><th>Description</th></tr>')
        for field in t['fields']:
            f.write('<tr><td>%s</td><td>%s</td><td>%s</td></tr>\n' % (field['name'], format_type(field['type']), field['description']))
        f.write('</table>')
    else:
        f.write('<p>Unknown class of custom type: %s</p>\n' % (t['type']))

f.write('<h2>Functions</h2>\n')
for func_desc in functions:
    paramlist = [param['name'] for param in func_desc['params']]
    paramlist = ', '.join(paramlist)

    f.write('<a name="func-%s"></a>\n' % (func_desc['name']))
    f.write("<h3>%s</h3>\n" % (func_desc['name']))
    f.write('<div class="description">%s(%s) - %s</div>' % (func_desc['name'], paramlist, func_desc['description']))
    f.write('<p>Parameters:</p>')
    f.write('<table>')
    for param in func_desc['params']:
        f.write('<tr><td>%s:</td><td><div class="typename">%s</div></td><td>-</td><td class="description">%s</td></tr>\n' % (param['name'], format_type(param['type']), param['description']))
    f.write('</table>')

    f.write('<p>Returns: <span class="typename">%s</span> - <span class="description">%s</span></p>\n' % (format_type(func_desc['result_type']), func_desc['result_desc']))

f.write('</body>\n</html>')
f.close()
