# Background Tasks / Progress
# Execute background functions while incrementing a #progress bar.
# #background_tasks
# ---
import asyncio
import time
import concurrent.futures
from threading import Event
from h2o_wave import main, app, Q, ui


# This takes a lot of time (compute heavy).
def blocking_function(q: Q, loop: asyncio.AbstractEventLoop):
    count = 0
    total = 10
    future = None
    while count < total:
        # Check if cancelled.
        if q.client.event.is_set():
            asyncio.ensure_future(show_cancel(q), loop=loop)
            return
            # This blocks the main thread and prevents any other execution.
        # This would be the compute in the real world.
        time.sleep(1)
        count += 1
        # If future is not done yet, skip the update to keep the correct order.
        if not future or future.done():
            # Assume you are able to emit some kind of progress.
            future = asyncio.ensure_future(update_ui(q, count / total), loop=loop)


async def show_cancel(q: Q):
    q.page['form'].progress.caption = 'Cancelled'
    await q.page.save()


async def update_ui(q: Q, value: int):
    q.page['form'].progress.value = value
    await q.page.save()


@app('/demo')
async def serve(q: Q):
    # Unimportant, draw initial UI.
    if not q.client.initialized:
        q.page['form'] = ui.form_card(box='1 1 3 2', items=[
            ui.inline([
                ui.button(name='start_job', label='Start job'),
                ui.button(name='cancel', label='Cancel')
            ]),
            ui.progress(name='progress', label='Progress', value=0),
        ])
        q.client.initialized = True

    # Handle start job button click.
    if q.args.start_job:
        # Do not run like this - will block the whole thread - freeze the app.
        # blocking_function(q, loop)

        # Get the current event loop - will be used for
        # running async functions within the blocking.
        loop = asyncio.get_event_loop()
        # Create an event to use for cancellation.
        q.client.event = Event()
        with concurrent.futures.ThreadPoolExecutor() as pool:
            await q.exec(pool, blocking_function, q, loop)

    if q.args.cancel:
        q.client.event.set()

    await q.page.save()