#pragma once

#include <map>
#include <string>
#include <tuple>
#include <vector>
#include <set>
#include "graph_loops.h"
#include "graph_calls.h"
#include "../ParallelizationRegions/ParRegions.h"
#include "../SageAnalysisTool/depInterfaceExt.h"
#include "AstWrapper.h"
#include "SgUtils.h"

#include "dvm.h"

typedef std::pair<std::pair<int, int>, std::pair<int, int>> attrType;
namespace DIST = Distribution;

enum REGIME { DATA_DISTR, COMP_DISTR, REMOTE_ACC, ARRAY_ACC_CORNER, SHARED_MEMORY_PAR, UNDEF };

// loop_analyzer.cpp
bool checkExistence(SgExpression *exp, const std::string& doName);

void loopAnalyzer(SgFile *file, 
                  std::vector<ParallelRegion*> &regions,
                  std::map<std::tuple<int, std::string, std::string>, DIST::Array*> &createdArrays,
                  std::vector<Messages> &messagesForFile,
                  REGIME regime,
                  const std::map<std::string, std::vector<FuncInfo*>> &allFuncInfo,
                  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,
                  const std::map<DIST::Array*, std::set<DIST::Array*>> &arrayLinksByFuncCalls, 
                  const std::map<SgStatement*, std::vector<DefUseList>> &defUseByPlace,
                  bool skipDeps, std::vector<LoopGraph*> *loopGraph = NULL);

void fillFromModule(SgSymbol* s, const std::map<std::string, std::set<std::string>>& privatesByModule, std::set<std::string>& privates);

std::vector<std::pair<Expression*, Expression*>> getArraySizes(std::vector<std::pair<int, int>>& sizes, SgSymbol* symb, SgStatement* decl,
                                                               const std::map<DIST::Array*, std::set<DIST::Array*>>& arrayLinksByFuncCalls,
                                                               const std::map<std::string, std::vector<FuncInfo*>>& allFuncInfo);

void arrayAccessAnalyzer(SgFile *file, std::vector<Messages> &messagesForFile, 
                         const std::map<std::tuple<int, std::string, std::string>, std::pair<DIST::Array*, DIST::ArrayAccessInfo*>> &declaredArrays, 
                         REGIME regime);

void processLoopInformationForFunction(std::map<LoopGraph*, std::map<DIST::Array*, ArrayInfo*>> &loopInfo);
void addToDistributionGraph(const std::map<LoopGraph*, std::map<DIST::Array*, ArrayInfo*>> &loopInfo, const std::map<DIST::Array*, std::set<DIST::Array*>> &arrayLinksByFuncCalls);

void printBlanks(const int sizeOfBlank, const int countOfBlanks);
bool isIntrinsic(const char *funName);

std::tuple<int, std::string, std::string> getUniqName(const std::map<std::string, SgStatement*> &commonBlocks, SgStatement *decl, SgSymbol *symb);
std::string getShortName(const std::tuple<int, std::string, std::string> &uniqKey);

void insertSpfAnalysisBeforeParalleLoops(const std::vector<LoopGraph*> &loops);
void recalculateArraySizes(std::set<DIST::Array*> &arraysDone, const std::set<DIST::Array*> &allArrays, const std::map<DIST::Array*, std::set<DIST::Array*>> &arrayLinksByFuncCalls, const std::map<std::string, std::vector<FuncInfo*>>& allFuncInfo);
int getSizeOfType(SgType* t);

// dep_analyzer.cpp
void tryToFindDependencies(LoopGraph *currLoop, const std::map<int, std::pair<SgForStmt*, std::pair<std::set<std::string>, std::set<std::string>>>> &allLoops,
                           std::set<SgStatement*> &funcWasInit, SgFile *file, std::vector<ParallelRegion*> regions, std::vector<Messages> *currMessages,
                           std::map<SgExpression*, std::string> &collection, const std::map<std::string, FuncInfo*> &allFuncs,
                           const std::map<SgStatement*, std::vector<DefUseList>> &defUseByPlace);
depGraph *getDependenciesGraph(const LoopGraph *currLoop, SgFile *file, const std::set<std::string> *privVars = NULL);

// allocations_prepoc.cpp
void preprocess_allocates(SgFile *file);
void moveAllocatesInterproc(const std::map<DIST::Array*, std::set<DIST::Array*>>& arrayLinksByFuncCalls);

// spf_directive_preproc.cpp
bool preprocess_spf_dirs(SgFile *file, const std::map<std::string, CommonBlock*> &commonBlocks, std::vector<Messages> &messagesForFile, const std::set<std::string>& allFileNames, std::map<std::pair<std::string, int>, std::set<SgStatement*>>& usersDirectives);
bool check_par_reg_dirs(SgFile *file, std::vector<Messages> &messagesForFile);

void revertion_spf_dirs(SgFile *file, 
                        std::map<std::tuple<int, std::string, std::string>, std::pair<DIST::Array*, DIST::ArrayAccessInfo*>> declaredArrays,
                        std::map<SgStatement*, std::set<std::tuple<int, std::string, std::string>>> declaratedArraysSt);
void addAcrossToLoops(LoopGraph *topLoop, const std::map<SgSymbol*, std::tuple<int, int, int>> &acrossToAdd, 
                      const std::map<int, SgForStmt*> &allLoops, 
                      std::vector<Messages> &currMessages);
void addPrivatesToLoops(LoopGraph *currLoop, const std::vector<const depNode*> &privatesToAdd, 
                        const std::map<int, SgForStmt*> &allLoops, 
                        std::vector<Messages> &currMessages);
void addReductionsToLoops(LoopGraph *currLoop, const std::vector<const depNode*> &reductionsToAdd, 
                          const std::map<int, SgForStmt*> &allLoops, 
                          std::vector<Messages> &currMessages);
void fillVars(SgExpression *exp, const std::set<int> &types, std::set<SgSymbol*> &identifierList, std::vector<SgExpression*> &funcCalls);

// TODO: from remote_access.cpp, may be need to move
bool isNeedToConvertIfCondition(SgExpression* ex);
void addRemotesToDir(const std::pair<SgForStmt*, LoopGraph*> *under_dvm_dir, const std::map<std::string, ArrayRefExp*> &uniqRemotes);
void fillRemoteFromDir(SgExpression* ex, std::vector<SgExpression*>& remotes);

template<int NUM> bool createRemoteDir(SgStatement *st, const std::map<std::string, FuncInfo*>& funcMap, const std::map<int, LoopGraph*> &sortedLoopGraph, const DIST::Arrays<int> &allArrays, 
                                       const DataDirective &data, const std::vector<int> &currVar, const uint64_t regionId, std::vector<Messages> &currMessages,
                                       const std::map<DIST::Array*, std::set<DIST::Array*>> &arrayLinksByFuncCalls);
void groupActualAndRemote(SgFile* file, bool revert = false);

std::vector<SgStatement*> filterUserSpf(const std::vector<SgStatement*>& toFilter, bool with_omp = true);

// ArrayAnalysis.cpp
int getAllDeclaredArrays(SgFile *file, std::map<std::tuple<int, std::string, std::string>, std::pair<DIST::Array*, DIST::ArrayAccessInfo*>> &declaredArrays,
                         std::map<SgStatement*, std::set<std::tuple<int, std::string, std::string>>> &declaratedArraysSt, std::map<std::string, std::vector<Messages>> &currMessages,
                         const std::vector<ParallelRegion*> &regions, const std::map<std::string, int>& distrStateFromGUI);