#include "cfgpass.h"
#include <QString>

using namespace std;
using namespace xreate;

CFGPass::CFGPass(ClaspLayer* claspLayer)
    : clasp(claspLayer)
{
}

void
CFGPass::process(const Expression& entity,const Context& context)
{
    switch(entity.__state)
    {
        case Expression::COMPOUND:
        {
            switch (entity.__op){
                    // First of all process all parameters
                case Operator::CALL : case Operator::LIST: case Operator::LOOP:
                    for (const Expression& op: entity.operands)
                    {
                        process(op, context);
                    }
            }

            // this is what we are looking for
            if (entity.__op == Operator::CALL)
            {
                const std::string& calleeName = entity.__valueS;
                assert(clasp->ast->__indexFunctions.count(calleeName));
                FID calleeId = clasp->ast->__indexFunctions.at(calleeName);

                __graph.addLink(context.id, calleeId);
                break;
            }

            if (entity.__op == Operator::LOOP)
            {
                assert(entity.blocks.size());
                process(&(entity.blocks[0]), context);
            }

            break;
        }

        case Expression::IDENT:
            const std::string& name = entity.__valueS;
                //search in current scope:
            if (context.scope->__vartable.count(name))
            {
                VID ident = context.scope->__vartable.at(name);

                // if ident is scope arg then do nothing
                if (! context.scope->__declarations.count(ident))
                {
                    break;
                }

                    // if ident declared within current scope,
                const Expression& decl = context.scope->__declarations.at(ident);
                process(decl, context);
            }

                //search in parent scope
            if (context.scope->__parent)
            {
                Context context2 = context;
                context2.scope = context.scope->__parent;

                process(entity, context2);
            };
    }
}

void CFGPass:: process(const CodeScope* scope, Context context)
{
    context.scope = scope;
    process(scope->__body, context);
}

void
CFGPass::run()
{
    __graph = CFGraph();
    cout << "CFGPass::run" << endl;

    for(FID f=0, fcount= clasp->ast->getFunctionsCount(); f<fcount; ++f)
    {
        const Function& func = clasp->ast->getFunctionById(f);
        __graph.addNode(f, string(func.getName()));

        Context context{f, nullptr};
        process(&func.getEntryScope(), context);
    };

    clasp->addCFGData(std::move(__graph));
}
