// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only

/*!
    \example widgets/imagescaling
    \meta tags {TaskTree}
    \examplecategory {Task Tree}
    \title TaskTree Image Scaling
    \ingroup tasktree_examples
    \brief Demonstrates how to execute For loop's iterations in parallel
           to asynchronously download and scale images using TaskTree.

    This example shows how to use Qt TaskTree classes to download a collection
    of images from the network and scale them without blocking the UI. It
    demonstrates how to use the \l {QtTaskTree::}{For} element to run subtasks
    in a parallel loop over QList items, and how to use the
    \l {QtTaskTree::}{Storage} element to share data between tasks.

    This example provides an alternative implementation for the image scaling
    example in \l {Image Scaling}{Qt Concurrent}.

    \image tasktree-imagescaling-example.png "Image downloading and scaling."

    \section1 Application workflow

    When the user presses the \uicontrol {Add URLs} button, the following method
    runs:

    \snippet widgets/imagescaling/imagescaling.cpp 0

    The method runs \c DownloadDialog. When the user accepts the dialog, the
    method gets the list of selected URLs, clears the displayed images, and
    prepares a grid for new images by calling \c initLayout(urls.size()).

    For each item in the \c urls list, the app:
    \list
        \li Downloads the image for a given address using
            \l {QtTaskTree::} {QNetworkReplyWrapper} and stores the received
            \l QByteArray data locally.
        \li Uses \l {QtTaskTree::} {QThreadFunction} to convert the
            \l QByteArray data to a \l QImage and scales it to a thumbnail
            in a separate thread.
        \li Displays the resulting thumbnail in the grid.
    \endlist

    To iterate over the \c urls list, the app uses an \c iterator element of
    \l {QtTaskTree::}{ListIterator} type. The \l QByteArray data received
    after downloading is stored in the \c storage element of
    \l {QtTaskTree::}{Storage}<QByteArray> type, which is used to
    pass the data to the scale function.

    \snippet widgets/imagescaling/imagescaling.cpp 1

    The \c recipe that describes the workflow looks like this:

    \snippet widgets/imagescaling/imagescaling.cpp 2

    Here is a summary of all elements used in the recipe:

    \table
    \header
        \li Element
        \li Purpose
    \row
        \li \l {QtTaskTree::} {For}
        \li Top level loop iterating over a list of \c urls.
    \row
        \li \l {QtTaskTree::} {Do}
        \li Describes the body of each iteration.
    \row
        \li \l {QtTaskTree::} {finishAllAndSuccess}
        \li Instructs the running tree to continue execution
            even on errors, and to report success afterward.
    \row
        \li \l {QtTaskTree::} {parallel}
        \li Causes that all \l {QtTaskTree::} {Do}'s body direct
            children will execute in parallel, including all
            iterations of the top level \l {QtTaskTree::} {For} element.

            This means that all iterations will start simultaneously.
    \row
        \li \l {QtTaskTree::} {onGroupSetup}(onRootSetup)
        \li Invokes \c onRootSetup handler when the first iteration
            is about to be executed.
    \row
        \li \l {QtTaskTree::} {onGroupDone}(onRootDone)
        \li Invokes \c onRootDone handler when all the iterations are finished.
    \row
        \li \l {QtTaskTree::} {Group}
        \li A subgroup, executed for each iteration.
            By default, all its children execute in sequence.
    \row
        \li \c storage
        \li Instructs the running task tree to instantiate the underlaying
            QByteArray instance when the subgroup is entered.

            This means the running task tree instantiates
            a separate QByteArray copy for each iteration.
    \row
        \li QNetworkReplyWrapperTask
        \li The first asynchronous task executed in sequence for each iteration.

            Starts downloading the QByteArray data for a given \c url.
            When the download is about to start, the \c onDownloadSetup
            is invoked to setup the input \c url for download.

            When it's finished, the \c onDownloadDone is invoked.
            In case of successful execution, the downloaded QByteArray data
            is stored inside \c storage element.
    \row
        \li QThreadFunctionTask<QImage>
        \li The second asynchronous task executed in sequence for each iteration.
            It's executed only after successful execution of the first task.

            Executes a function in a separate thread, passing the stored QByteArray
            data of the downloaded image. When the function is about to start,
            the \c onScaleSetup is invoked to setup the QByteArray data for scaling.

            When it's finished, the \c onScaleDone is invoked.
            In case of successful execution it displays the thumbnail in a grid.
    \endtable

    \section1 Update the status bar

    The following shows how all handlers are implemented:

    \snippet widgets/imagescaling/imagescaling.cpp 3

    When the first iteration starts, the \c onRootSetup handler updates the
    status bar message and enables the \uicontrol Cancel button. When the last
    iteration finishes, the \c onRootDone handler updates the status bar
    message and disables the \uicontrol Cancel button.

    \section1 Download and scale images

    Each iteration runs in parallel with others and consists of two sequential
    tasks. The first task in each iteration is
    \l {QtTaskTree::} {QNetworkReplyWrapperTask}.
    Here is how its setup and done handlers are implemented:

    \snippet widgets/imagescaling/imagescaling.cpp 4

    The \c onDownloadSetup handler runs when
    \l {QtTaskTree::} {QNetworkReplyWrapper} is about to
    start. It sets up the network access manager and network request. The
    handler captures the \c iterator, which gives access to the current address
    from the \c urls list.

    The \c onDownloadDone handler runs when
    \l {QtTaskTree::} {QNetworkReplyWrapper} finishes. It
    captures the \c storage and \c iterator. If the task succeeds, it stores
    the received \l QByteArray data in \c storage. The task tree creates a
    separate \l QByteArray instance for each iteration. Dereferencing \c storage
    gives access to the \l QByteArray instance for the current iteration. If the
    task fails or is canceled, the handler shows the error message for the
    label immediately. The index of the current iteration is returned by
    \c {iterator.iteration()}.

    The second task in each iteration is
    \l {QtTaskTree::} {QThreadFunctionTask}<QImage>. Its
    setup and done handlers are as follows:

    \snippet widgets/imagescaling/imagescaling.cpp 5

    Inside \c onScaleSetup, the handler schedules a call to the
    \c scale function, passing the \l QByteArray data received from the first
    task as an argument. This is done by dereferencing the captured \c storage.
    The \c scale function runs in a separate thread.

    The \c onScaleDone handler runs when the \c scale function finishes.
    \c onScaleDone is called from the main thread. If scaling succeeds, the
    handler assigns the scaled image to the appropriate label. Otherwise, the
    handler shows an error message.

    The following shows how the \c scale function is implemented:

    \snippet widgets/imagescaling/imagescaling.cpp 6

    The function takes a \l QPromise to the result type as an argument. The
    \c promise is used to return the final result or to issue an error by
    canceling the promise's future.

    \include examples-run.qdocinc
*/
