#pragma once

#include "dvm.h"
#include "utils.h"
#include "../Distribution/Distribution.h"
#include "graph_calls.h"
#include "../DynamicAnalysis/gcov_info.h"
#include "module_utils.h"

SgStatement* declaratedInStmt(SgSymbol *toFind, std::vector<SgStatement*> *allDecls = NULL, bool printInternal = true, SgStatement* scope = NULL);

#include "DefUseList.h"
#include "CommonBlock.h"

std::string removeIncludeStatsAndUnparse(SgFile *file, const char *fileName, const char *fout, std::set<std::string> &allIncludeFiles, bool outFree, const std::map<std::string, std::set<std::string>> &moduleUsesByFile, const std::map<std::string, std::string>& moduleDelcs, const std::map<SgStatement*, std::vector<SgStatement*>>& exctactedModuleStats, bool toString, bool renameIncludes, bool dontReplaceIncls);
SgSymbol* findSymbolOrCreate(SgFile *file, const std::string toFind, SgType *type = NULL, SgStatement *scope = NULL);
void recExpressionPrint(SgExpression *exp);
void removeSubstrFromStr(std::string &str, const std::string &del);

void tryToFindPrivateInAttributes(SgStatement* st, std::set<std::string>& privatesVars, bool onlyReduction = false, bool onlyUsers = false);

void fillNonDistrArraysAsPrivate(SgStatement *st,
                                 const std::map<std::tuple<int, std::string, std::string>, std::pair<DIST::Array*, DIST::ArrayAccessInfo*>> &declaredArrays,
                                 const std::map<SgStatement*, std::set<std::tuple<int, std::string, std::string>>> &declaratedArraysSt,
                                 std::set<std::string> &privatesVars);

DIST::Array* getArrayFromDeclarated(SgStatement *st, const std::string &arrayName);

void initTags();
bool isDVM_stat(SgStatement *st);
bool isSPF_stat(SgStatement *st);
bool isEqExpressions(SgExpression *left, SgExpression *right, std::map<SgExpression*, std::string> &collection);
void getCommonBlocksRef(std::map<std::string, std::vector<SgExpression*>> &commonBlocks, SgStatement *start, SgStatement *end, const std::string *nameToSkip = NULL);
int checkCommonInMainUnit(SgProject& project, std::map<std::string, std::vector<Messages>>& SPF_messages, const std::set<DIST::Array*>& arrays, bool forDistrbuted);

std::tuple<int, std::string, std::string> getFromUniqTable(SgSymbol *symb);
std::tuple<int, std::string, std::string> getUniqName(const std::map<std::string, std::vector<SgExpression*>> &commonBlocks, SgStatement *decl, SgSymbol *symb);
SgStatement* findMainUnit(SgProject *proj, std::map<std::string, std::vector<Messages>>& SPF_messages);

template<typename IN_TYPE, typename OUT_TYPE>
const std::vector<OUT_TYPE> getAttributes(IN_TYPE st, const std::set<int> dataType);

template<typename IN_TYPE>
void deleteAttributes(IN_TYPE st, const std::set<int> dataType);

void constructDefUseStep1(SgFile *file, std::map<std::string, std::vector<DefUseList>> &defUseByFunctions, std::map<std::string, std::vector<FuncInfo*>> &allFuncInfo, std::vector<Messages> &messages);
void constructDefUseStep2(SgFile *file, std::map<std::string, std::vector<DefUseList>> &defUseByFunctions);
std::set<std::string> getAllDefVars(const std::string &funcName);
std::set<std::string> getAllUseVars(const std::string &funcName);
const std::vector<DefUseList>& getAllDefUseVarsList(const std::string &funcName);
const std::vector<DefUseList> getAllDefUseVarsList(const std::string &funcName, const std::string varName);
int printDefUseSets(const char *fileName, const std::map<std::string, std::vector<DefUseList>> &defUseLists);
int printCommonBlocks(const char *fileName, const std::map<std::string, CommonBlock*> &commonBlocks);
void groupDeclarations(SgFile *file);

bool ifSymbolExists(SgFile *file, const std::string &symbName);
int checkSymbNameAndCorrect(const std::string& symbName, int complite);
std::string checkSymbNameAndCorrect(const std::string& symbName, const std::string complite = "_");
const CommonBlock* isArrayInCommon(const std::map<std::string, CommonBlock*> &commonBlocks, const DIST::Array *array);

std::vector<DIST::Array*> fillArraysFromDir(Statement *st);

void printSymbolTable(SgFile* file, std::string filter = "", const std::set<int>& vars = {});
SgStatement* getFuncStat(SgStatement *st, const std::set<int> additional = std::set<int>());
std::map<SgStatement*, std::vector<DefUseList>> createDefUseMapByPlace();
SgStatement* duplicateProcedure(SgStatement* toDup, const std::string* newName, bool withAttributes = false, bool withComment = false, bool withSameLines = true, bool dontInsert = false);

SgExpression* makeExprList(const std::vector<SgExpression*>& items, bool withSort = true);
std::string unparseProjectToString(SgFile* file, const int curr_regime);

SgStatement* makeDeclaration(SgStatement* curr, const std::vector<SgSymbol*>& s, std::vector<SgExpression*>* inits = NULL);
std::vector<SgStatement*> makeDeclaration(const std::vector<SgSymbol*>& symbolsToDeclare, SgStatement* where, std::vector<SgExpression*>* inits = NULL);

int getNextFreeLabel();

void fillVisibleInUseVariables(SgStatement *useSt, std::map<std::string, SgSymbol*> &vars);

std::string preprocDataString(std::string data, bool full = true);
std::map<std::string, std::string> splitData(const std::set<SgValueExp*>& dataStats);

void extractComments(SgStatement* where, const std::string& what);

void getVariables(SgExpression* ex, std::set<std::string>& variables, const std::set<int> variants);
void getVariables(SgExpression* ex, std::set<SgSymbol*>& variables, const std::set<int> variants);

template<typename T>
std::set<T> getAllVariables(SgStatement* stFrom, SgStatement* stTo, const std::set<int>& variants);

SgProject* createProject(const char* proj_name, std::vector<ParallelRegion*>& parallelRegions, 
                         std::vector<ParallelRegion*>& subs_parallelRegions,
                         std::map<std::string, std::vector<SgStatement*>>& hiddenData, 
                         std::map<std::string, int>& filesNameWithoutExt, 
                         std::map<std::string, std::set<std::string>>& moduleUsesByFile,
                         std::map<std::string, std::string>& moduleDecls, 
                         std::map<std::string, std::map<SgStatement*, std::vector<SgStatement*>>>& exctactedModuleStats, bool printSymbTable);

bool isArrayType(SgType* type);
bool isArrayRef(SgExpression* ex);
bool isStringArrayType(SgType* type);

void moveLabelBefore(SgStatement* st, SgStatement* next = NULL);

bool isEqSymbols(SgSymbol* sym1, SgSymbol* sym2);
std::set<std::string> getAllFilesInProject();

void LogIftoIfThen(SgStatement* stmt);
void removeSpecialCommentsFromProject(SgFile* file);

void getMaxMinBlockDistribution(SgFile* file, std::pair<int, int>& min_max);

void addPrivatesToArraysFromGUI(SgFile* file, const std::map<std::tuple<int, std::string, std::string>, std::pair<DIST::Array*, DIST::ArrayAccessInfo*>>& declaredArrays, const std::map<std::string, int>& distrStateFromGUI);

void shiftLines(SgProject* project, bool print = true);

template<typename T>
static inline std::multimap<std::string, T> setToMapWithSortByStr(const std::set<T>& setIn)
{
    std::multimap<std::string, T> retMap;
    for (auto& elem : setIn)
        retMap.insert(make_pair(elem->identifier(), elem));
    return retMap;
}