/*
 * Copyright 2009- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation
 * nor does it submit to any jurisdiction.
 */

#include "ecflow/simulator/FlatAnalyserVisitor.hpp"

#include "ecflow/core/Indentor.hpp"
#include "ecflow/core/Str.hpp"
#include "ecflow/node/Defs.hpp"
#include "ecflow/node/ExprAst.hpp"
#include "ecflow/node/Family.hpp"
#include "ecflow/node/Suite.hpp"
#include "ecflow/node/Task.hpp"
#include "ecflow/simulator/AstAnalyserVisitor.hpp"

using namespace std;

namespace ecf {

///////////////////////////////////////////////////////////////////////////////
FlatAnalyserVisitor::FlatAnalyserVisitor() = default;

void FlatAnalyserVisitor::visitDefs(Defs* d) {
    for (suite_ptr s : d->suiteVec()) {
        s->acceptVisitTraversor(*this);
    }
}

void FlatAnalyserVisitor::visitSuite(Suite* s) {
    visitNodeContainer(s);
}
void FlatAnalyserVisitor::visitFamily(Family* f) {
    visitNodeContainer(f);
}

void FlatAnalyserVisitor::visitNodeContainer(NodeContainer* nc) {
    if (nc->state() == NState::COMPLETE)
        return;

    Indentor in;
    bool traverseChildren = analyse(nc);

    // Dont bother traversing children if parent is holding on trigger/complete expression
    if (traverseChildren) {
        for (node_ptr t : nc->nodeVec()) {
            t->acceptVisitTraversor(*this);
        }
    }
}

void FlatAnalyserVisitor::visitTask(Task* t) {
    Indentor in;
    analyse(t);
}

bool FlatAnalyserVisitor::analyse(Node* node) {
    bool traverseChildren = true;

    Indentor::indent(ss_) << node->debugType() << Str::COLON() << node->name() << " state("
                          << NState::toString(node->state()) << ")";
    if (node->state() != NState::COMPLETE) {

        if (node->repeat().isInfinite()) {
            ss_ << " may **NEVER** complete due to " << node->repeat().toString();
        }
        ss_ << "\n";

        if (node->state() == NState::QUEUED) {
            std::vector<std::string> theReasonWhy;
            node->why(theReasonWhy);
            for (const auto& i : theReasonWhy) {
                Indentor::indent(ss_) << "Reason: " << i << "\n";
            }
        }

        /// Note a complete expression that does not evaluate, does *NOT* hold the node
        /// It merly sets node to complete.
        if (node->completeAst() && !node->evaluateComplete()) {
            Indentor::indent(ss_) << "holding on complete expression '" << node->completeExpression() << "'\n";

            AstAnalyserVisitor astVisitor;
            node->completeAst()->accept(astVisitor);
            for (const string& nodePath : astVisitor.dependentNodePaths()) {
                Indentor in;
                Indentor::indent(ss_) << "'" << nodePath << "' is not defined in the expression\n";
            }
            ss_ << *node->completeAst();

            traverseChildren = false;
        }

        if (node->triggerAst() && !node->evaluateTrigger()) {
            Indentor::indent(ss_) << "holding on trigger expression '" << node->triggerExpression() << "'\n";

            AstAnalyserVisitor astVisitor;
            node->triggerAst()->accept(astVisitor);
            for (const string& nodePath : astVisitor.dependentNodePaths()) {
                Indentor in;
                Indentor::indent(ss_) << "'" << nodePath << "' is not defined in the expression\n";
            }
            ss_ << *node->triggerAst();

            traverseChildren = false;
        }
    }
    ss_ << "\n";
    return traverseChildren;
}

} // namespace ecf
