#!/usr/bin/env python3
""" Run the webapp server. """

import os
import threading
import urllib.request
import time
import glob

import click

# ---------------------------------------------------------------------

@click.command()
@click.option( "--addr","-a","bind_addr", help="Webapp server address (host:port)." )
@click.option( "--force-init-delay", default=0, help="Force the webapp to initialize (#seconds delay)." )
@click.option( "--debug","flask_debug", is_flag=True, default=False, help="Run Flask in debug mode." )
def main( bind_addr, force_init_delay, flask_debug ):
    """Run the vasl-templates webapp server."""

    # initialize
    from vasl_templates.webapp import app
    flask_port = None
    if bind_addr:
        words = bind_addr.split( ":" )
        flask_host = words[0]
        if len(words) > 1:
            flask_port = words[1]
    else:
        flask_host = app.config.get( "FLASK_HOST", "localhost" )
    if not flask_port:
        flask_port = app.config.get( "FLASK_PORT_NO" )
    if not flask_debug:
        flask_debug = app.config.get( "FLASK_DEBUG", False )

    # validate the configuration
    if not flask_host:
        raise RuntimeError( "The server host was not set." )
    if not flask_port:
        raise RuntimeError( "The server port was not set." )

    # monitor extra files for changes
    extra_files = []
    fspecs = [ "static/", "templates/", "config/" ]
    fspecs.extend( [ "tests/control_tests_servicer.py", "tests/proto/generated/" ] )
    for fspec in fspecs:
        fspec = os.path.abspath( os.path.join( os.path.dirname(__file__), fspec ) )
        if os.path.isdir( fspec ):
            files = [ os.path.join(fspec,f) for f in os.listdir(fspec) ]
            files = [ f for f in files if os.path.isfile(f) and os.path.splitext(f)[1] not in [".swp"] ]
        else:
            files = glob.glob( fspec )
        extra_files.extend( files )

    # check if we should force webapp initialization
    if force_init_delay > 0:
        def _start_server():
            """Force the server to do "first request" initialization."""
            # NOTE: This is not needed when running the desktop app (since it will request the home page),
            # but if we're running just the server (i.e. from the console, or a Docker container), then
            # it's useful to send a request (any request), since this will trigger "first request" initialization
            # (in particular, starting the download thread).
            time.sleep( force_init_delay )
            url = "http://{}:{}/ping".format( flask_host, flask_port )
            with urllib.request.urlopen( url ) as resp:
                _ = resp.read()
        threading.Thread( target=_start_server, daemon=True ).start()

    # run the server
    if flask_debug:
        # NOTE: It's useful to run the webapp using the Flask development server, since it will
        # automatically reload itself when the source files change.
        app.run(
            host=flask_host, port=flask_port,
            debug=flask_debug,
            extra_files=extra_files
        )
    else:
        import waitress
        # FUDGE! Browsers tend to send a max. of 6-8 concurrent requests per server, so we increase
        # the number of worker threads to avoid task queue warnings :-/
        nthreads = app.config.get( "WAITRESS_THREADS", 8 )
        waitress.serve( app,
            host=flask_host, port=flask_port,
            threads=nthreads
        )


# ---------------------------------------------------------------------

if __name__ == "__main__":
    main() #pylint: disable=no-value-for-parameter
