# Licensed under a 3-clause BSD style license - see LICENSE.rst

import errno
import http.server
import os
import random
import socketserver

from .. import util
from ..console import log
from . import Command


def random_ports(port, n):
    """Generate a list of n random ports near the given port.

    The first 5 ports will be sequential, and the remaining n-5 will be
    randomly selected in the range [port-2*n, port+2*n].
    """
    if port != 0:
        yield port
    else:
        port = 8080
        for i in range(min(5, n)):
            yield port + i
        for i in range(n - 5):
            yield max(1, port + random.randint(-2 * n, 2 * n))


def create_httpd(handler_cls, port=0):
    # Create a server that allows address reuse
    class MyTCPServer(socketserver.ThreadingTCPServer):
        allow_reuse_address = True
        daemon_threads = True

    for port in random_ports(port, 5):
        try:
            httpd = MyTCPServer(("", port), handler_cls)
            base_url = f"http://127.0.0.1:{port}/"
            break
        except OSError as e:
            if e.errno == errno.EADDRINUSE:
                continue
            else:
                raise
    else:
        raise util.UserError("Failed to find an unused port for serving web pages")

    return httpd, base_url


class Preview(Command):
    @classmethod
    def setup_arguments(cls, subparsers):
        parser = subparsers.add_parser(
            "preview",
            help="Preview the results using a local web server",
            description="Preview the results using a local web server",
        )

        parser.add_argument(
            "--port", "-p", type=int, default=0, help="Port to run webserver on.  [8080]"
        )
        parser.add_argument("--browser", "-b", action="store_true", help="Open in webbrowser")
        parser.add_argument(
            '--html-dir',
            '-o',
            default=None,
            help=("Optional output directory. Default is 'html_dir' from asv config"),
        )

        parser.set_defaults(func=cls.run_from_args)

        return parser

    @classmethod
    def run_from_conf_args(cls, conf, args):
        if args.html_dir:
            conf.html_dir = args.html_dir
        return cls.run(conf=conf, port=args.port, browser=args.browser)

    @classmethod
    def run(cls, conf, port=0, browser=False):
        os.chdir(conf.html_dir)

        class Handler(http.server.SimpleHTTPRequestHandler):
            def translate_path(self, path):
                path = http.server.SimpleHTTPRequestHandler.translate_path(self, path)
                return util.long_path(path)

        httpd, base_url = create_httpd(Handler, port=port)

        log.info(f"Serving at {base_url}")

        if browser:
            import webbrowser

            webbrowser.open(base_url)

        log.info("Press ^C to abort\n")
        try:
            httpd.serve_forever()
        except KeyboardInterrupt:
            return
