root/src/apps/haikudepot/process/ProcessCoordinatorFactory.cpp
/*
 * Copyright 2018-2026, Andrew Lindesay <apl@lindesay.co.nz>.
 * All rights reserved. Distributed under the terms of the MIT License.
 */
#include "ProcessCoordinatorFactory.h"

#include <AutoLocker.h>
#include <Autolock.h>

#include <package/Context.h>
#include <package/PackageRoster.h>

#include "AbstractServerProcess.h"
#include "CacheScreenshotProcess.h"
#include "DeskbarLink.h"
#include "HaikuDepotConstants.h"
#include "IncrementViewCounterProcess.h"
#include "InstallPackageProcess.h"
#include "LocalPkgDataLoadProcess.h"
#include "LocalRepositoryUpdateProcess.h"
#include "Logger.h"
#include "Model.h"
#include "OpenPackageProcess.h"
#include "PackageInfoListener.h"
#include "PopulatePkgChangelogFromServerProcess.h"
#include "PopulatePkgSizesProcess.h"
#include "PopulatePkgUserRatingsFromServerProcess.h"
#include "ProcessCoordinator.h"
#include "ServerHelper.h"
#include "ServerIconExportUpdateProcess.h"
#include "ServerPkgDataUpdateProcess.h"
#include "ServerReferenceDataUpdateProcess.h"
#include "ServerRepositoryDataUpdateProcess.h"
#include "ServerSettings.h"
#include "StorageUtils.h"
#include "ThreadedProcessNode.h"
#include "UninstallPackageProcess.h"
#include "UserDetailVerifierProcess.h"


using namespace BPackageKit;


/*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::CreateIncrementViewCounter(Model* model, const PackageInfoRef package)
{
        ProcessCoordinator* processCoordinator = new ProcessCoordinator("IncrementViewCounter");
        AbstractProcessNode* node
                = new ThreadedProcessNode(new IncrementViewCounterProcess(model, package));
        processCoordinator->AddNode(node);
        return processCoordinator;
}


/*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::CreateUserDetailVerifierCoordinator(
        UserDetailVerifierListener* userDetailVerifierListener, Model* model)
{
        ProcessCoordinator* processCoordinator = new ProcessCoordinator("UserDetailVerifier");
        AbstractProcessNode* userDetailVerifier
                = new ThreadedProcessNode(new UserDetailVerifierProcess(model, userDetailVerifierListener));
        processCoordinator->AddNode(userDetailVerifier);
        return processCoordinator;
}


/* static */ ProcessCoordinator*
ProcessCoordinatorFactory::CreateBulkLoadCoordinator(Model* model, bool forceLocalUpdate)
{
        bool areWorkingFilesAvailable = StorageUtils::AreWorkingFilesAvailable();
        uint32 serverProcessOptions = _CalculateServerProcessOptions();
        ProcessCoordinator* processCoordinator
                = new ProcessCoordinator("BulkLoad", new BMessage(MSG_BULK_LOAD_DONE));

        AbstractProcessNode* localRepositoryUpdate
                = new ThreadedProcessNode(new LocalRepositoryUpdateProcess(model, forceLocalUpdate));
        processCoordinator->AddNode(localRepositoryUpdate);

        AbstractProcessNode* localPkgDataLoad
                = new ThreadedProcessNode(new LocalPkgDataLoadProcess(model, forceLocalUpdate));
        localPkgDataLoad->AddPredecessor(localRepositoryUpdate);
        processCoordinator->AddNode(localPkgDataLoad);

        if (areWorkingFilesAvailable) {
                AbstractProcessNode* serverIconExportUpdate = new ThreadedProcessNode(
                        new ServerIconExportUpdateProcess(model, serverProcessOptions));
                serverIconExportUpdate->AddPredecessor(localPkgDataLoad);
                processCoordinator->AddNode(serverIconExportUpdate);

                AbstractProcessNode* serverRepositoryDataUpdate = new ThreadedProcessNode(
                        new ServerRepositoryDataUpdateProcess(model, serverProcessOptions));
                serverRepositoryDataUpdate->AddPredecessor(localPkgDataLoad);
                processCoordinator->AddNode(serverRepositoryDataUpdate);

                AbstractProcessNode* serverReferenceDataUpdate = new ThreadedProcessNode(
                        new ServerReferenceDataUpdateProcess(model, serverProcessOptions));
                processCoordinator->AddNode(serverReferenceDataUpdate);

                // This one has to run after the server data is taken up because it
                // will fill in the gaps based on local data that was not able to be
                // sourced from the server. It has all of the
                // `ServerPkgDataUpdateProcess` nodes configured as its predecessors.

                AbstractProcessNode* populatePkgSizes
                        = new ThreadedProcessNode(new PopulatePkgSizesProcess(model));

                // create a process for each of the repositories that are configured on
                // the local system.  Later, only those that have a web-app repository
                // server code will be actually processed, but this means that the
                // creation of the 'processes' does not need to be dynamic as the
                // process coordinator runs.

                BPackageRoster roster;
                BStringList repoNames;
                status_t repoNamesResult = roster.GetRepositoryNames(repoNames);

                if (repoNamesResult == B_OK) {
                        for (int32 i = 0; i < repoNames.CountStrings(); i++) {
                                AbstractProcessNode* processNode
                                        = new ThreadedProcessNode(new ServerPkgDataUpdateProcess(repoNames.StringAt(i),
                                                model, serverProcessOptions));
                                processNode->AddPredecessor(serverRepositoryDataUpdate);
                                processNode->AddPredecessor(serverReferenceDataUpdate);
                                processCoordinator->AddNode(processNode);

                                populatePkgSizes->AddPredecessor(processNode);
                        }
                } else {
                        HDERROR("a problem has arisen getting the repository names.");
                }

                processCoordinator->AddNode(populatePkgSizes);
        }

        return processCoordinator;
}


/*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::CacheScreenshotCoordinator(Model* model,
        ScreenshotCoordinate& screenshotCoordinate)
{
        return _CreateSingleProcessCoordinator("CacheScreenshot",
                new CacheScreenshotProcess(model, screenshotCoordinate));
}


/*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::PopulatePkgChangelogCoordinator(Model* model, const BString& packageName)
{
        return _CreateSingleProcessCoordinator("PopulatePkgChangelog",
                new PopulatePkgChangelogFromServerProcess(packageName, model));
}


/*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::PopulatePkgUserRatingsCoordinator(Model* model,
        const BString& packageName)
{
        return _CreateSingleProcessCoordinator("PopulatePkgUserRatings",
                new PopulatePkgUserRatingsFromServerProcess(packageName, model));
}


/*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::CreateInstallPackageActionCoordinator(Model* model,
        const InstallPackageAction& action)
{
        ProcessCoordinator* processCoordinator
                = new ProcessCoordinator("InstallPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
        AbstractProcessNode* processNode
                = new ThreadedProcessNode(new InstallPackageProcess(action.PackageName(), model), 10);
        processCoordinator->AddNode(processNode);
        return processCoordinator;
}


/*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::CreateUninstallPackageActionCoordinator(Model* model,
        const UninstallPackageAction& action)
{
        ProcessCoordinator* processCoordinator
                = new ProcessCoordinator("UninstallPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
        AbstractProcessNode* processNode
                = new ThreadedProcessNode(new UninstallPackageProcess(action.PackageName(), model), 10);
        processCoordinator->AddNode(processNode);
        return processCoordinator;
}


/*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::CreateOpenPackageActionCoordinator(Model* model,
        const OpenPackageAction& action)
{
        ProcessCoordinator* processCoordinator
                = new ProcessCoordinator("OpenPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
        AbstractProcessNode* processNode = new ThreadedProcessNode(
                new OpenPackageProcess(action.PackageName(), model, action.Link()));
        processCoordinator->AddNode(processNode);
        return processCoordinator;
}


/* static */ uint32
ProcessCoordinatorFactory::_CalculateServerProcessOptions()
{
        uint32 processOptions = 0;

        if (ServerSettings::IsClientTooOld()) {
                HDINFO("bulk load proceeding without network communications because the client is too old");
                processOptions |= SERVER_PROCESS_NO_NETWORKING;
        }

        if (!ServerHelper::IsNetworkAvailable())
                processOptions |= SERVER_PROCESS_NO_NETWORKING;

        if (ServerSettings::PreferCache())
                processOptions |= SERVER_PROCESS_PREFER_CACHE;

        if (ServerSettings::DropCache())
                processOptions |= SERVER_PROCESS_DROP_CACHE;

        return processOptions;
}


/*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::_CreateSingleProcessCoordinator(const char* name,
        AbstractProcess* process)
{
        ProcessCoordinator* processCoordinator = new ProcessCoordinator(name);
        AbstractProcessNode* cacheScreenshotNode = new ThreadedProcessNode(process);
        processCoordinator->AddNode(cacheScreenshotNode);
        return processCoordinator;
}