""" Load the main vasl-templates program.

vasl-templates can be slow to start (especially on Windows), since it has to unpack the PyInstaller-generated EXE,
then startup Qt. We want to show a splash screen while all this happening, but we can't just put it in vasl-templates,
since it would only happen *after* all the slow stuff has finished :-/ So, we have this stub program that shows
a splash screen, launches the main vasl-templates program, and waits for it to finish starting up.
"""

import sys
import os
import subprocess
import threading
import itertools
import urllib.request
from urllib.error import URLError
import time
import configparser

# NOTE: It's important that this program start up quickly (otherwise it becomes pointless),
# so we use tkinter, instead of PyQt (and also avoid bundling a 2nd copy of PyQt :-/).
import tkinter
import tkinter.messagebox

if getattr( sys, "frozen", False ):
    BASE_DIR = sys._MEIPASS #pylint: disable=no-member,protected-access
else:
    BASE_DIR = os.path.abspath( os.path.dirname( __file__ ) )

STARTUP_TIMEOUT = 60 # how to long to wait for vasl-templates to start (seconds)

main_window = None

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

def main( args ):
    """Load the main vasl-templates program."""

    # initialize Tkinter
    global main_window
    main_window = tkinter.Tk()
    main_window.option_add( "*Dialog.msg.font", "Helvetica 12" )

    # load the app icon
    # NOTE: This image file doesn't exist in source control, but is created dynamically from
    # the main app icon by the freeze script, and inserted into the PyInstaller-generated executable.
    # We do things this way so that we don't have to bundle Pillow into the release.
    app_icon = tkinter.PhotoImage(
        file = make_asset_path( "app-icon.png" )
    )

    # locate the main vasl-templates executable
    fname = os.path.join( os.path.dirname( sys.executable ), "vasl-templates-main" )
    if sys.platform == "win32":
        fname += ".exe"
    if not os.path.isfile( fname ):
        show_error_msg( "Can't find the main vasl-templates program.", withdraw=True )
        return -1

    # launch the main vasl-templates program
    try:
        proc = subprocess.Popen( itertools.chain( [fname], args ) ) #pylint: disable=consider-using-with
    except Exception as ex: #pylint: disable=broad-except
        show_error_msg( "Can't start vasl-templates:\n\n{}".format( ex ), withdraw=True )
        return -2

    # get the webapp port number
    port = 5010
    fname = os.path.join( os.path.dirname( fname ), "config/app.cfg" )
    if os.path.isfile( fname ):
        config_parser = configparser.ConfigParser()
        config_parser.optionxform = str # preserve case for the keys :-/
        config_parser.read( fname )
        args = dict( config_parser.items( "System" ) )
        port = args.get( "FLASK_PORT_NO", port )

    # create the splash window
    create_window( app_icon )

    # start a background thread to check on the main vasl-templates process
    threading.Thread(
        target = check_startup,
        args = ( proc, port )
    ).start()

    # run the main loop
    main_window.mainloop()

    return 0

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

def create_window( app_icon ):
    """Create the splash window."""

    # create the splash window
    main_window.geometry( "290x75" )
    main_window.title( "vasl-templates loader" )
    main_window.overrideredirect( 1 ) # nb: "-type splash" doesn't work on Windows :-/
    main_window.eval( "tk::PlaceWindow . center" )
    main_window.wm_attributes( "-topmost", 1 )
    main_window.tk.call( "wm", "iconphoto", main_window._w, app_icon ) #pylint: disable=protected-access
    main_window.protocol( "WM_DELETE_WINDOW", lambda: None )

    # add the app icon
    label = tkinter.Label( main_window, image=app_icon )
    label.grid( row=0, column=0, rowspan=2, padx=5, pady=5 )

    # add the caption
    label = tkinter.Label( main_window, text="Loading vasl-templates...", font=("Helvetica",12) )
    label.grid( row=0, column=1, padx=5, pady=(5,0) )

    # add the "loading" image (we have to animate it ourself :-/)
    anim_label = tkinter.Label( main_window )
    anim_label.grid( row=1, column=1, sticky=tkinter.N, padx=0, pady=0 )
    fname = make_asset_path( "loading.gif" )
    nframes = 13
    frames = [
        tkinter.PhotoImage( file=fname, format="gif -index {}".format( i ) )
        for i in range(nframes)
    ]
    frame_index = 0
    def next_frame():
        nonlocal frame_index
        frame = frames[ frame_index ]
        frame_index = ( frame_index + 1 ) % nframes
        anim_label.configure( image=frame )
        main_window.after( 75, next_frame )
    main_window.after( 0, next_frame )

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

def check_startup( proc, port ):
    """Check the startup of the main vasl-templates process."""

    def do_check():

        # check if we've waited for too long
        if time.time() - start_time > STARTUP_TIMEOUT:
            # yup - give up
            raise RuntimeError( "Couldn't start vasl-templates." )

        # check if the main vasl-templates process has gone away
        if proc.poll() is not None:
            raise RuntimeError( "The vasl-templates program ended unexpectedly." )

        # check if the webapp is responding
        url = "http://localhost:{}/ping".format( port )
        try:
            with urllib.request.urlopen( url ) as resp:
                _ = resp.read()
        except URLError:
            # no response - the webapp is probably still starting up
            return False
        except Exception as ex: #pylint: disable=broad-except
            raise RuntimeError( "Couldn't communicate with vasl-templates:\n\n{}".format( ex ) ) from ex

        # the main vasl-templates program has started up and is responsive - our job is done!
        if sys.platform == "win32":
            # FUDGE! There is a short amount of time between the webapp server starting and
            # the main window appearing. We delay here for a bit, to try to synchronize
            # our window fading out with the main vasl-templates window appearing.
            time.sleep( 1 )
        return True

    def on_done( msg ):
        if msg:
            show_error_msg( msg, withdraw=True )
        fade_out( main_window, main_window.quit )

    # run the main loop
    start_time = time.time()
    while True:
        try:
            if do_check():
                on_done( None )
                break
        except Exception as ex: #pylint: disable=broad-except
            on_done( str(ex) )
            return
        time.sleep( 0.25 )

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

def fade_out( target, on_done ):
    """Fade out the target window."""
    alpha = target.attributes( "-alpha" )
    if alpha > 0:
        alpha -= 0.1
        target.attributes( "-alpha", alpha )
        target.after( 50, lambda: fade_out( target, on_done ) )
    else:
        on_done()

def make_asset_path( fname ):
    """Generate the path to an asset file."""
    return os.path.join( BASE_DIR, "assets", fname )

def show_error_msg( error_msg, withdraw=False ):
    """Show an error dialog."""
    if withdraw:
        main_window.withdraw()
    tkinter.messagebox.showinfo( "vasl-templates loader error", error_msg )

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

if __name__ == "__main__":
    sys.exit( main( sys.argv[1:] ) )
