#pragma once

#include "SgUtils.h"
#include "CFGraph.h"

namespace LIVE_VARIABLES
{
    /* Store information about live and dead variables after call operator */
    /* (needed for interprocedural part of live variable analysis)         */
    class LiveDeadVarsForCall
    {
    private:
        bool tryInsert(std::set<SAPFOR::BasicBlock*>& dest, SAPFOR::BasicBlock* b);
    public:
        FuncInfo* func;

        std::map<int, std::set<SAPFOR::BasicBlock*>> live_after;
        std::set<int> dead_after;

        std::map<SAPFOR::Argument*, std::set<SAPFOR::BasicBlock*>> commons_live_after;
        std::set<SAPFOR::Argument*> commons_dead_after;

        std::vector<SAPFOR::Argument*> params;
        SAPFOR::BasicBlock* block;

        LiveDeadVarsForCall(FuncInfo* f, SAPFOR::BasicBlock* b, const std::vector<SAPFOR::Argument*>& p);

        void make_live(SAPFOR::Argument* arg, SAPFOR::BasicBlock* b);
        void make_dead(SAPFOR::Argument* arg);

        void updateFromOut();
    };
}

template <class IT, class DEST>
void insertIfVar(IT begin, IT end, DEST& to) {
    for (auto it = begin; it != end; it++)
        if (*it && (*it)->getType() == SAPFOR::CFG_ARG_TYPE::VAR)
            to.insert(*it);
}

void getUseDefForInstruction(SAPFOR::BasicBlock* block, SAPFOR::Instruction* instr,
                             std::set<SAPFOR::Argument*>& use, std::set<SAPFOR::Argument*>& def,
                             std::vector<SAPFOR::Argument*>& formal_parameters, std::vector<LIVE_VARIABLES::LiveDeadVarsForCall>& fcalls,
                             std::vector<SAPFOR::Argument*>& arg_stack, int& arg_stack_index, int& arg_stack_size,
                             bool& last_stack_op,
                             std::string& fName, const std::map<std::string, FuncInfo*>& funcByName, bool interprocedural);

void runLiveVariableAnalysis(const std::map<FuncInfo*, std::vector<SAPFOR::BasicBlock*>>& CFGraph_for_project);

void doDumpLive(const std::map<FuncInfo*, std::vector<SAPFOR::BasicBlock*>>& CFGraph_for_project);

bool joinGlobalsWithParameters(const std::vector<SAPFOR::Argument*>& params,
                               const std::map<std::string, std::pair<std::set<int>, std::set<SAPFOR::Argument*>>>& params_and_globals,
                               const std::string& func_name, std::set<SAPFOR::Argument*>& result);