/**
 * UGENE - Integrated Bioinformatics Tools.
 * Copyright (C) 2008-2023 UniPro <ugene@unipro.ru>
 * http://ugene.net
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 */

#pragma once

#include <assert.h>

#include <QList>
#include <QMap>
#include <QObject>
#include <QString>
#include <QVariant>

#include <U2Core/IdRegistry.h>
#include <U2Core/Task.h>

#include <U2Lang/Schema.h>
#include <U2Lang/WorkflowContext.h>
#include <U2Lang/WorkflowTransport.h>

namespace U2 {

class WorkflowDebugStatus;

namespace Workflow {

/**
 * Worker represents actor at runtime
 * takes input data from and puts output data to CommunicationChannels
 *
 * base class
 */
class U2LANG_EXPORT Worker {
public:
    virtual ~Worker() = default;

    // initialize input and output ports
    virtual void init() = 0;
    // if data in the ports is ready
    virtual bool isReady() const = 0;
    // get data from actor and return task
    // if you want your worker support scripting:
    // use BaseWorker::getMessageAndSetupScriptValues to take data from port
    virtual Task* tick() = 0;
    // nothing to do
    virtual bool isDone() const = 0;
    // opened files, etc...
    virtual void cleanup() = 0;

    void setContext(WorkflowContext* newContext) {
        context = newContext;
    }

protected:
    WorkflowContext* context = nullptr;

};  // Worker

/**
 * runtime state of worker
 */
enum WorkerState {
    WorkerWaiting,
    WorkerReady,
    WorkerRunning,
    WorkerDone,
    WorkerPaused
};  // WorkerState

/**
 * Worker for whole schema
 * can decide if workers can run in parallel or any other analysis
 */
class U2LANG_EXPORT Scheduler : public Worker {
public:
    Scheduler(Schema* sch)
        : schema(sch), lastTask(nullptr) {
    }
    virtual WorkerState getWorkerState(const ActorId&) = 0;
    virtual Task* replayLastWorkerTick() = 0;
    // returning value indicates if current task was canceled
    virtual bool cancelCurrentTaskIfAllowed() = 0;
    virtual void makeOneTick(const ActorId&) = 0;
    virtual void setDebugInfo(WorkflowDebugStatus* newDebugInfo) {
        Q_ASSERT(newDebugInfo != nullptr);
        debugInfo = newDebugInfo;
    }

protected:
    Schema* schema;
    Task* lastTask;
    WorkflowDebugStatus* debugInfo;

    virtual WorkerState getWorkerState(const Actor*) = 0;
};  // Scheduler

/**
 * represents controller between actor and runtime realizations of actor (Worker)
 * is a container of such controllers (inherits IdRegistry<DomainFactory>)
 * represents set of tasks of some computational domain
 *
 * says how to execute your worker (Scheduler and CommunicationChannel)
 * Usage: create execution domain factory (e.g. LocalDomainFactory) and register there your tasks factories
 */
class U2LANG_EXPORT DomainFactory : public IdRegistry<DomainFactory>, public Descriptor {
public:
    DomainFactory(const Descriptor& d)
        : Descriptor(d) {
    }
    DomainFactory(const QString& id)
        : Descriptor(id) {
    }
    virtual ~DomainFactory() {
    }

    // computational tasks domain
    virtual Worker* createWorker(Actor*) = 0;

    // execution domain
    virtual CommunicationChannel* createConnection(Link*) {
        return nullptr;
    }
    virtual Scheduler* createScheduler(Schema*) {
        return nullptr;
    }
    virtual void destroy(Scheduler*, Schema*) {
    }
    static void addParametersSetToMap(QVariantMap& map, const QString& attrValue, const QStringList& parametersList) {
        QVariantMap parametersMap;
        foreach (const QString& curStr, parametersList) {
            parametersMap[curStr] = curStr;
        }
        map.insert(attrValue, parametersMap);
    }

};  // DomainFactory

/**
 * standard registry of factories
 * Usage: register here execution domain factories (see usage of DomainFactory)
 */
class U2LANG_EXPORT DomainFactoryRegistry : public IdRegistry<DomainFactory> {
};  // DomainFactoryRegistry

}  // namespace Workflow

}  // namespace U2
