#!/usr/bin/env python3
"""
Serves a directory over HTTP and publishes itself via DNS-SD.
"""


import getopt
import os
import http.server
import socketserver
import sys

try:
    import avahi
    import dbus
    has_avahi = True
except:
    has_avahi = False


__version__ = "0.1.2"


USAGE = "Usage: %s [-p<port>] [-d<dir>] [-t<title>]"

# Hack to force it to serve UTF-8.
m = http.server.SimpleHTTPRequestHandler.extensions_map
m[""] = "text/plain"
m.update(dict((k, v + ";charset=UTF-8") for k, v in list(m.items())))


def avahi_get(obj_path, iface):
    obj = dbus.SystemBus().get_object(avahi.DBUS_NAME, obj_path)
    return dbus.Interface(obj, iface)


def avahi_make_entry_group():
    svr = avahi_get("/", avahi.DBUS_INTERFACE_SERVER)
    return avahi_get(svr.EntryGroupNew(), avahi.DBUS_INTERFACE_ENTRY_GROUP)


def avahi_add_service(group, svc_type, port, name="", txt_records=None):
    if txt_records is None:
        txt_records = []
    group.AddService(
        avahi.IF_UNSPEC,
        avahi.PROTO_UNSPEC,
        dbus.UInt32(0),
        name,
        svc_type,
        "",
        "",
        dbus.UInt16(port),
        txt_records,
    )


def serve(port, title):
    httpd = socketserver.TCPServer(("", port), http.server.SimpleHTTPRequestHandler)

    _, port = httpd.server_address
    print(f"Starting server on port {port}...")

    if has_avahi:
        print(f"Registering with Avahi as '{title}'")
        group = avahi_make_entry_group()
        avahi_add_service(group, "_http._tcp", port, title)
        group.Commit()

    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print("Shut down.")


def usage(message=None):
    """
    Prints usage information and possibly an error message.
    """
    fd = sys.stdout
    exit_code = 0
    if message:
        fd = sys.stderr
        exit_code = 2
        print(message, file=fd)
    name = os.path.basename(sys.argv[0])
    print(USAGE % (name,), file=fd)
    sys.exit(exit_code)


def main():
    try:
        opts, _ = getopt.getopt(sys.argv[1:], "p:d:t:h")
    except getopt.GetoptError as err:
        usage(err)

    port = 0
    title = "Shared directory"
    for opt, val in opts:
        if opt == "-p":
            port = int(val)
            if 0 > port >= 65536:
                usage(f"Bad port number: {port}")
        elif opt == "-t" and len(val) > 0:
            title = val
        elif opt == "-d":
            if not os.path.isdir(val):
                usage(f"Bad directory path: '{val}'")
            os.chdir(val)
        elif opt == "-h":
            usage()
        else:
            usage(f"Unknown flag: {opt}")

    serve(port, title)


if __name__ == "__main__":
    main()
