#include "Array.h"
#include "errors.h"
#include "utils.h"
#include "graph_calls.h"
#include "SgUtils.h"
#include "../DirectiveProcessing/directive_parser.h"
#include "../DirectiveProcessing/directive_omp_parser.h"
#include "../LoopAnalyzer/loop_analyzer.h"

using namespace std;

extern int ignoreIO;
extern map<DIST::Array*, std::tuple<int, string, string>> tableOfUniqNamesByArray;
static set<tuple<int, string, string>> checkedArraysForWrongLocation;

static bool findOmpThreadPrivDecl(SgStatement* st, map<SgStatement*, set<string>>& ompThreadPrivate, SgSymbol* toFind)
{

    auto it = ompThreadPrivate.find(st);
    if (it == ompThreadPrivate.end())
    {
        it = ompThreadPrivate.insert(it, make_pair(st, set<string>()));

        SgStatement* lastN = st->lastNodeOfStmt();
        set<string> dummy;
        do
        {
            st = st->lexNext();

            auto res = parseOmpInStatement(st, dummy);
            for (auto& dir : res)
                for (auto& var : dir.threadPrivVars)
                    it->second.insert(var);
        } while (st != lastN && !isSgExecutableStatement(st) && st->variant() != CONTAINS_STMT);
    }

    if (it->second.find(toFind->identifier()) != it->second.end())
        return true;
    else
        return false;
}

static bool hasAssingOpInDecl(SgSymbol* symb)
{
    vector<SgStatement*> allDecls;
    SgStatement* decl = declaratedInStmt(symb, &allDecls);

    for (auto& elem : allDecls)
    {
        if (elem->variant() == VAR_DECL_90)
        {
            SgExpression* list = elem->expr(0);
            while (list)
            {
                if (list->lhs()->variant() == ASSGN_OP)
                    if (list->lhs()->lhs()->symbol() && OriginalSymbol(list->lhs()->lhs()->symbol()) == symb)
                        return true;
                list = list->rhs();
            }
        }
    }

    return false;
}

static string getNameWithScope(SgStatement* scope, const string& currFunctionName)
{
    if (scope && isSgProgHedrStmt(scope) && scope->symbol()->identifier() != currFunctionName)
        return scope->symbol()->identifier();
    else
        return currFunctionName;
}

struct findInfo
{
    findInfo(const string fName, SgExpression* ex, int parN, bool isWrite) :
        fName(fName), ex(ex), parN(parN), isWrite(isWrite)
    { }

    SgExpression* ex;
    string fName;
    int parN;
    bool isWrite;
};

static void findArrayRefs (SgExpression* ex, SgStatement* st, string fName, int parN, bool isWrite,
                           const map<string, vector<SgExpression*>>& commonBlocks,
                           map<tuple<int, string, string>, pair<DIST::Array*, DIST::ArrayAccessInfo*>>& declaredArrays,
                           map<SgStatement*, set<tuple<int, string, string>>>& declaratedArraysSt,
                           const set<string>& privates, const set<string>& deprecatedByIO,
                           bool isExecutable, const string& currFunctionName,
                           const vector<string>& inRegion,
                           const set<string>& funcParNames,
                           map<SgStatement*, set<string>>& ompThreadPrivate,
                           const map<string, int>& distrStateFromGUI,
                           const bool saveAllLocals,
                           map<string, vector<Messages>>& currMessages,
                           int& errorCount)
{
    const string globalFile = current_file->filename();
    const set<string> filesInProj = getAllFilesInProject();

    if (ex == NULL)
        return;
    stack<findInfo> queue;
    queue.push(findInfo(fName, ex, parN, isWrite));

    while (!queue.empty())
    {
        const findInfo& curQ = queue.top();
        ex = curQ.ex;
        fName = curQ.fName;
        parN = curQ.parN;
        isWrite = curQ.isWrite;

        queue.pop();

        if (isArrayRef(ex))
        {
            SgSymbol* symb = OriginalSymbol(ex->symbol());
            const bool inDataStat = (symb->attributes() & DATA_BIT) != 0 || ((ex->symbol())->attributes() & DATA_BIT);
            checkNull(symb->type(), convertFileName(__FILE__).c_str(), __LINE__);

            const int typeSize = getSizeOfType(symb->type()->baseType());
            if (typeSize == 0)
            {
                //__spf_print(1, "Wrong type size for array %s\n", symb->identifier());
                //printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            }

            SgStatement* decl = declaratedInStmt(symb);
            if (decl->variant() == DVM_VAR_DECL || decl->variant() == HPF_TEMPLATE_STAT)
            {
                const string tmp(decl->unparse());
                if (tmp.find("!DVM$ TEMPLATE") != string::npos)
                {
                    auto sTemp = symb->identifier();
                    tuple<int, string, string> uniqKey;
                    bool found = false;
                    for (auto& elem : declaredArrays)
                    {
                        if (elem.second.first->GetShortName() == sTemp)
                        {
                            uniqKey = elem.first;
                            found = true;
                        }
                    }

                    if (found)
                    {
                        auto itDecl = declaratedArraysSt.find(decl);
                        if (itDecl == declaratedArraysSt.end())
                            itDecl = declaratedArraysSt.insert(itDecl, make_pair(decl, set<tuple<int, string, string>>()));
                        itDecl->second.insert(uniqKey);

                        return;
                    }
                }
            }

            auto uniqKey = getUniqName(commonBlocks, decl, symb);

            SgStatement* scope = symb->scope();
            pair<DIST::arrayLocType, string> arrayLocation;

            string typePrefix = "";
            while (scope && scope->variant() == STRUCT_DECL)
            {
                if (typePrefix == "")
                    typePrefix = scope->symbol()->identifier();
                else
                    typePrefix += scope->symbol()->identifier() + string("::");
                scope = scope->controlParent();
            }

            if ((ex->symbol() && symb != ex->symbol()) || (scope && scope->variant() == MODULE_STMT))
            {
                if (scope)
                {
                    string modName = scope->symbol()->identifier();
                    arrayLocation = make_pair(DIST::l_MODULE, (typePrefix == "") ? modName : modName + "::" + typePrefix);
                }
                else //TODO: find module name with another way
                    arrayLocation = make_pair(DIST::l_MODULE, "UNREC_MODULE_NAME");
            }
            else if (get<1>(uniqKey).find("common_") != string::npos)
                arrayLocation = make_pair(DIST::l_COMMON, get<1>(uniqKey).substr(strlen("common_")));
            else if (funcParNames.find(symb->identifier()) != funcParNames.end())
                arrayLocation = make_pair(DIST::l_PARAMETER, getNameWithScope(scope, currFunctionName));
            else
            {
                if (saveAllLocals || ((symb->attributes() & SAVE_BIT) != 0) || hasAssingOpInDecl(symb))
                    arrayLocation = make_pair(DIST::l_LOCAL_SAVE, getNameWithScope(scope, currFunctionName));
                else
                    arrayLocation = make_pair(DIST::l_LOCAL, getNameWithScope(scope, currFunctionName));
            }

            auto itNew = declaredArrays.find(uniqKey);
            if (itNew == declaredArrays.end())
            {
                DIST::Array* arrayToAdd =
                    new DIST::Array(getShortName(uniqKey), symb->identifier(), ((SgArrayType*)(symb->type()))->dimension(),
                        getUniqArrayId(), decl->fileName(), decl->lineNumber(), arrayLocation, new Symbol(symb),
                        findOmpThreadPrivDecl(scope, ompThreadPrivate, symb), false, false,
                        inRegion, typeSize, sharedMemoryParallelization ? DIST::NO_DISTR : DIST::DISTR);

                itNew = declaredArrays.insert(itNew, make_pair(uniqKey, make_pair(arrayToAdd, new DIST::ArrayAccessInfo())));

                vector<pair<int, int>> sizes;
                map<DIST::Array*, set<DIST::Array*>> arrayLinksByFuncCallsNotReady;
                map<string, vector<FuncInfo*>> allFuncInfoNoReady;
                auto sizesExpr = getArraySizes(sizes, symb, decl, arrayLinksByFuncCallsNotReady, allFuncInfoNoReady);
                arrayToAdd->SetSizes(sizes);
                arrayToAdd->SetSizesExpr(sizesExpr);
                tableOfUniqNamesByArray[arrayToAdd] = uniqKey;
            }
            else // check the same location from include!
            {
                auto prevLocation = itNew->second.first->GetLocation();
                if (prevLocation != arrayLocation)
                {
                    if (checkedArraysForWrongLocation.find(uniqKey) == checkedArraysForWrongLocation.end())
                    {
                        checkedArraysForWrongLocation.insert(uniqKey);
                        __spf_print(1, "can not change declaration area of array '%s' on line %d\n", symb->identifier(), st->lineNumber());

                        wstring messageE, messageR;
                        __spf_printToLongBuf(messageE, L"Array '%s' has declaration area conflict, it might be worth applying the Include inlining pass", to_wstring(symb->identifier()).c_str());
                        __spf_printToLongBuf(messageR, R184, to_wstring(symb->identifier()).c_str());

                        currMessages[st->fileName()].push_back(Messages(ERROR, st->lineNumber(), messageR, messageE, 1061));
                    }
                    errorCount++;
                }
            }

            if ((symb->attributes() & EQUIVALENCE_BIT) != 0)
                itNew->second.first->SetEquvalence(true);

            for (auto& reg : inRegion)
                itNew->second.first->SetRegionPlace(reg);

            const auto oldVal = itNew->second.first->GetDistributeFlagVal();
            bool isArrayInModule = (itNew->second.first->GetLocation().first == DIST::l_MODULE);

            if (oldVal == DIST::DISTR || oldVal == DIST::NO_DISTR)
            {
                if (itNew->second.first->IsOmpThreadPrivate())
                    itNew->second.first->SetDistributeFlag(DIST::SPF_PRIV);
                else if (deprecatedByIO.find(symb->identifier()) != deprecatedByIO.end())
                    itNew->second.first->SetDistributeFlag(DIST::IO_PRIV);
                else if (isArrayInModule || privates.find(symb->identifier()) != privates.end())
                {
                    //check in module
                    if (itNew->second.first->GetLocation().first == DIST::l_MODULE)
                    {
                        for (auto& data : getAttributes<SgStatement*, SgStatement*>(decl, set<int>{ SPF_ANALYSIS_DIR }))
                        {
                            set<string> privatesS;
                            fillPrivatesFromComment(new Statement(data), privatesS);
                            if (privatesS.find(symb->identifier()) != privatesS.end())
                            {
                                itNew->second.first->SetDistributeFlag(DIST::SPF_PRIV);
                                break;
                            }
                        }

                        auto prev = decl->lexPrev();
                        checkNull(prev, convertFileName(__FILE__).c_str(), __LINE__);

                        set<string> privatesS;
                        fillPrivatesFromComment(new Statement(prev), privatesS);
                        if (privatesS.find(symb->identifier()) != privatesS.end())
                            itNew->second.first->SetDistributeFlag(DIST::SPF_PRIV);
                    }
                    else
                        itNew->second.first->SetDistributeFlag(DIST::SPF_PRIV);
                }
                else if (isSgConstantSymb(symb) || inDataStat)
                    itNew->second.first->SetDistributeFlag(DIST::SPF_PRIV);
                else
                {
                    auto it = distrStateFromGUI.find(itNew->second.first->GetIndepUniqName());
                    if (it != distrStateFromGUI.end())
                    {
                        if (it->second != oldVal)
                        {
                            itNew->second.first->SetDistributeFlag((DIST::distFlag)it->second);
                            __spf_print(1, "change flag for array from cache '%s': %d -> %d\n", it->first.c_str(), oldVal, it->second);
                        }
                    }
                    else
                        itNew->second.first->SetDistributeFlag(DIST::DISTR);
                }
            }

            if (typeSize == 0) // unknown
                itNew->second.first->SetDistributeFlag(DIST::SPF_PRIV);


            if (!isExecutable)
                itNew->second.first->AddDeclInfo(make_pair(st->fileName(), st->lineNumber()), filesInProj, globalFile, new Symbol(symb));

            if (isExecutable)
            {
                if (st->variant() != ALLOCATE_STMT && st->variant() != DEALLOCATE_STMT)
                {
                    itNew->second.second->AddAccessInfo(st->fileName(), make_pair(st->lineNumber(), isWrite ? 1 : 0), fName, parN);
                    itNew->second.first->AddUsagePlace(st->fileName(), st->lineNumber());
                }
            }

            auto itDecl = declaratedArraysSt.find(decl);
            if (itDecl == declaratedArraysSt.end())
                itDecl = declaratedArraysSt.insert(itDecl, make_pair(decl, set<tuple<int, string, string>>()));
            itDecl->second.insert(uniqKey);

            if (decl->variant() == DVM_VAR_DECL || decl->variant() == HPF_TEMPLATE_STAT)
            {
                const string tmp(decl->unparse());
                if (tmp.find("!DVM$ TEMPLATE") != string::npos)
                {
                    itNew->second.first->SetTemplateFlag(true);
                    //TODO: analyze align mapping
                    for (int z = 0; z < itNew->second.first->GetDimSize(); ++z)
                        itNew->second.first->SetMappedDim(z);
                }
            }
        }

        if (ex->variant() == FUNC_CALL)
        {
            SgFunctionCallExp* funcExp = (SgFunctionCallExp*)ex;
            const string fName = funcExp->funName()->identifier();
            bool intr = (isIntrinsicFunctionName(fName.c_str()) == 1);
            for (int z = 0; z < funcExp->numberOfArgs(); ++z)
            {
                //assume all arguments of function as OUT, except for inctrinsics
                bool isWriteN = intr ? false : true;
                //need to correct W/R usage with GraphCall map later
                findArrayRefs(funcExp->arg(z), st, fName, z, isWriteN, commonBlocks, declaredArrays, declaratedArraysSt, privates, deprecatedByIO, isExecutable, currFunctionName, inRegion, funcParNames, ompThreadPrivate, distrStateFromGUI, saveAllLocals, currMessages, errorCount);
            }
        }
        else
        {
            bool isWriteN = false;
            if (ex->lhs())
                queue.push(findInfo("", ex->lhs(), -1, isWriteN));
            if (ex->rhs())
                queue.push(findInfo("", ex->rhs(), -1, isWriteN));
            //findArrayRefs(ex->lhs(), st, "", -1, isWriteN, commonBlocks, declaredArrays, declaratedArraysSt, privates, deprecatedByIO, isExecutable, currFunctionName, inRegion, funcParNames, ompThreadPrivate, distrStateFromGUI, saveAllLocals, currMessages, errorCount);
            //findArrayRefs(ex->rhs(), st, "", -1, isWriteN, commonBlocks, declaredArrays, declaratedArraysSt, privates, deprecatedByIO, isExecutable, currFunctionName, inRegion, funcParNames, ompThreadPrivate, distrStateFromGUI, saveAllLocals, currMessages, errorCount);
        }
    }
}

static void findArrayRefInIO(SgExpression* ex, set<string>& deprecatedByIO, SgStatement* st, map<string, vector<Messages>>& currMessages)
{
    if (ex)
    {
        if (ex->variant() == ARRAY_REF)
        {
            auto symb = ex->symbol();
            if (symb->type())
            {
                if (symb->type()->variant() == T_ARRAY)
                {
                    auto found = deprecatedByIO.find(OriginalSymbol(symb)->identifier());
                    if (found == deprecatedByIO.end())
                    {
                        deprecatedByIO.insert(found, OriginalSymbol(symb)->identifier());

                        wstring messageE, messageR;
                        __spf_printToLongBuf(messageE, L"Array '%s' can not be distributed because of DVM's I/O constraints", to_wstring(symb->identifier()).c_str());
                        __spf_printToLongBuf(messageR, R68, to_wstring(symb->identifier()).c_str());

                        currMessages[st->fileName()].push_back(Messages(WARR, st->lineNumber(), messageR, messageE, 1037));

                        __spf_print(1, "Array '%s' at line %d can not be distributed because of DVM's I/O constraints\n", symb->identifier(), st->lineNumber());
                    }
                }
            }
        }

        findArrayRefInIO(ex->lhs(), deprecatedByIO, st, currMessages);
        findArrayRefInIO(ex->rhs(), deprecatedByIO, st, currMessages);
    }
}

static void findReshape(SgStatement* st, set<string>& privates, map<string, vector<Messages>>& currMessages)
{
    if (st->variant() == ASSIGN_STAT)
    {
        SgExpression* exL = st->expr(0);
        SgExpression* exR = st->expr(1);

        if (exR->variant() == FUNC_CALL && exL->variant() == ARRAY_REF)
        {
            if (exR->symbol()->identifier() == string("reshape"))
            {
                if (privates.find(exL->symbol()->identifier()) == privates.end())
                {
                    privates.insert(exL->symbol()->identifier());

                    wstring messageE, messageR;
                    __spf_printToLongBuf(messageE, L"Array '%s' can not be distributed because of RESHAPE", to_wstring(exL->symbol()->identifier()).c_str());
                    __spf_printToLongBuf(messageR, R90, to_wstring(exL->symbol()->identifier()).c_str());
                    currMessages[st->fileName()].push_back(Messages(NOTE, st->lineNumber(), messageR, messageE, 1047));
                }
            }
        }
    }
}

static void findConstructorRef(SgStatement* st, set<string>& privates, map<string, vector<Messages>>& currMessages)
{
    if (st->variant() == ASSIGN_STAT)
    {
        SgExpression* exL = st->expr(0);
        SgExpression* exR = st->expr(1);

        if (exR->variant() == CONSTRUCTOR_REF && exL->variant() == ARRAY_REF)
        {
            if (privates.find(exL->symbol()->identifier()) == privates.end())
            {
                privates.insert(exL->symbol()->identifier());

                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"Array '%s' can not be distributed because of initializer list", to_wstring(exL->symbol()->identifier()).c_str());
                __spf_printToLongBuf(messageR, R164, to_wstring(exL->symbol()->identifier()).c_str());
                currMessages[st->fileName()].push_back(Messages(NOTE, st->lineNumber(), messageR, messageE, 1047));
            }
        }
    }
}

static void addPrivates(SgStatement* st, set<string>& privates, map<string, set<Symbol*>>& reductions,
                        map<string, set<tuple<Symbol*, Symbol*, int>>>& reductionsLoc)
{
    //after SPF preprocessing 
    for (auto& data : getAttributes<SgStatement*, SgStatement*>(st, set<int>{ SPF_ANALYSIS_DIR }))
    {
        set<Symbol*> privatesS;
        fillPrivatesFromComment(new Statement(data), privatesS);
        fillReductionsFromComment(new Statement(data), reductions);
        fillReductionsFromComment(new Statement(data), reductionsLoc);

        for (auto& elem : privatesS)
            privates.insert(elem->GetOriginal()->identifier());
    }

    //before SPF preprocessing 
    if (st->variant() == SPF_ANALYSIS_DIR)
    {
        set<Symbol*> privatesS;
        fillPrivatesFromComment(new Statement(st), privatesS);
        fillReductionsFromComment(new Statement(st), reductions);
        fillReductionsFromComment(new Statement(st), reductionsLoc);

        for (auto& elem : privatesS)
            privates.insert(elem->GetOriginal()->identifier());
    }
}

int getAllDeclaredArrays(SgFile* file, map<tuple<int, string, string>, pair<DIST::Array*, DIST::ArrayAccessInfo*>>& declaredArrays,
                         map<SgStatement*, set<tuple<int, string, string>>>& declaratedArraysSt, map<string, vector<Messages>>& currMessages,
                         const vector<ParallelRegion*>& regions, const map<string, int>& distrStateFromGUI)
{
    int countErrors = 0;

    vector<SgStatement*> modules;
    findModulesInFile(file, modules);

    map<string, set<string>> privatesByModule;
    for (auto& mod : modules)
    {
        const string modName = mod->symbol()->identifier();
        privatesByModule[modName] = set<string>();
        auto it = privatesByModule.find(modName);

        for (SgStatement* iter = mod; iter != mod->lastNodeOfStmt(); iter = iter->lexNext())
        {
            if (iter->variant() == CONTAINS_STMT)
                break;

            //after SPF preprocessing 
            for (auto& data : getAttributes<SgStatement*, SgStatement*>(iter, set<int>{ SPF_ANALYSIS_DIR }))
                fillPrivatesFromComment(new Statement(data), it->second);

            //before SPF preprocessing 
            if (iter->variant() == SPF_ANALYSIS_DIR)
                fillPrivatesFromComment(new Statement(iter), it->second);
        }
    }

    map<SgStatement*, set<string>> ompThreadPrivate;

    for (int i = 0; i < file->numberOfFunctions(); ++i)
    {
        bool saveAllLocals = false;

        SgStatement* st = file->functions(i);
        SgStatement* lastNode = st->lastNodeOfStmt();
        map<string, vector<SgExpression*>> commonBlocks;
        const string currFunctionName = st->symbol()->identifier();

        getCommonBlocksRef(commonBlocks, st, lastNode);
        set<string> privates;
        set<string> deprecatedByIO;
        map<string, set<Symbol*>> reductions;
        map<string, set<tuple<Symbol*, Symbol*, int>>> reductionsLoc;
        set<string> funcParNames;

        if (st->variant() != PROG_HEDR)
        {
            SgProcHedrStmt* func = (SgProcHedrStmt*)st;
            for (int z = 0; z < func->numberOfParameters(); ++z)
                funcParNames.insert(func->parameter(z)->identifier());

            if (func->nameWithContains() != func->name().identifier()) // added contains args
            {
                SgProcHedrStmt* cp = (SgProcHedrStmt*)func->controlParent();
                checkNull(cp, convertFileName(__FILE__).c_str(), __LINE__);

                for (int z = 0; z < cp->numberOfParameters(); ++z)
                    funcParNames.insert(cp->parameter(z)->identifier());
            }
        }

        for (SgStatement* iter = st; iter != lastNode; iter = iter->lexNext())
        {
            if (iter->variant() == CONTAINS_STMT)
                break;

            addPrivates(iter, privates, reductions, reductionsLoc);

            if (iter->variant() == USE_STMT)
                fillFromModule(iter->symbol(), privatesByModule, privates);

            if (iter->variant() == SAVE_DECL)
                if (!iter->expr(0) && !iter->expr(1) && !iter->expr(2))
                    saveAllLocals = true;
        }

        for (SgStatement* iter = st; iter != lastNode; iter = iter->lexNext())
        {
            if (iter->variant() == CONTAINS_STMT)
                break;

            findReshape(iter, privates, currMessages);
            findConstructorRef(iter, privates, currMessages);
        }

        SgStatement* tmpModFind = st;
        while (tmpModFind->variant() != GLOBAL)
        {
            tmpModFind = tmpModFind->controlParent();
            if (tmpModFind->variant() == MODULE_STMT)
                fillFromModule(tmpModFind->symbol(), privatesByModule, privates);
        }

        SgStatement* currF = st;
        SgStatement* contains = isSgProgHedrStmt(currF->controlParent());
        if (contains)
        {
            for (SgStatement* loc = contains; loc; loc = loc->lexNext())
            {
                if (isSgExecutableStatement(loc))
                    break;
                if (loc->variant() == CONTAINS_STMT)
                    break;

                if (loc->variant() == USE_STMT)
                    fillFromModule(loc->symbol(), privatesByModule, privates);
            }
        }

        for (auto& elem : reductions)
            for (auto& setElem : elem.second)
                privates.insert(setElem->identifier());

        for (auto& elem : reductionsLoc)
        {
            for (auto& setElem : elem.second)
            {
                privates.insert(get<0>(setElem)->identifier());
                privates.insert(get<1>(setElem)->identifier());
            }
        }

        //analyze IO operations
        if (!ignoreIO)
        {
            for (SgStatement* iter = st; iter != lastNode; iter = iter->lexNext())
            {
                if (iter->variant() == CONTAINS_STMT)
                    break;

                SgInputOutputStmt* stIO = isSgInputOutputStmt(iter);
                set<ParallelRegion*> currRegs = getAllRegionsByLine(regions, iter->fileName(), iter->lineNumber());
                if (stIO && currRegs.size()) // deprecate to distribute arrays only in regions
                {
                    int countOfItems = 0;
                    for (SgExpression* items = stIO->itemList(); items; items = items->rhs(), ++countOfItems);

                    //TODO: need to add more checkers!
                    if (countOfItems > 1)
                    {
                        for (SgExpression* items = stIO->itemList(); items; items = items->rhs())
                            findArrayRefInIO(items->lhs(), deprecatedByIO, stIO, currMessages);
                    }
                    else if (countOfItems == 1)
                    {
                        auto list = stIO->specList();
                        bool ok = true;
                        //exclude FMT='format'
                        while (list)
                        {
                            if (list->lhs() && list->lhs()->variant() == SPEC_PAIR)
                            {
                                auto ex = list->lhs();
                                if (ex->lhs() && ex->rhs())
                                {
                                    if (ex->lhs()->variant() == KEYWORD_VAL)
                                    {
                                        SgKeywordValExp* key = (SgKeywordValExp*)(ex->lhs());
                                        if (key->value() == string("fmt"))
                                            if (ex->rhs()->variant() == STRING_VAL)
                                                ok = false;
                                    }
                                }
                            }

                            if (!ok)
                                break;
                            list = list->rhs();
                        }

                        //check A(i,j) for example
                        auto item = stIO->itemList()->lhs();
                        if (item->rhs() || item->lhs())
                            ok = false;

                        if (!ok)
                            findArrayRefInIO(item, deprecatedByIO, stIO, currMessages);
                    }
                }
            }
        }

        while (st != lastNode)
        {
            if (st->variant() == CONTAINS_STMT)
                break;

            if (!isSPF_stat(st) && !isDVM_stat(st))
            {
                set<ParallelRegion*> currRegs = getAllRegionsByLine(regions, st->fileName(), st->lineNumber());
                vector<string> regNames;
                for (auto& reg : currRegs)
                    regNames.push_back(reg->GetName());
                if (regNames.size() == 0)
                    regNames.push_back("default");

                if (st->variant() == PROC_STAT)
                {
                    SgCallStmt* funcExp = (SgCallStmt*)st;
                    const string fName = funcExp->symbol()->identifier();
                    for (int z = 0; z < funcExp->numberOfArgs(); ++z)
                    {
                        findArrayRefs(funcExp->arg(z), st, fName, z, true,
                            commonBlocks, declaredArrays, declaratedArraysSt, privates, deprecatedByIO,
                            isSgExecutableStatement(st) ? true : false, currFunctionName,
                            regNames, funcParNames, ompThreadPrivate, distrStateFromGUI, saveAllLocals,
                            currMessages, countErrors);
                    }
                }
                else
                {
                    for (int i = 0; i < 3; ++i)
                        findArrayRefs(st->expr(i), st, "", -1, (st->variant() == ASSIGN_STAT && i == 0) ? true : false,
                            commonBlocks, declaredArrays, declaratedArraysSt, privates, deprecatedByIO,
                            isSgExecutableStatement(st) ? true : false, currFunctionName,
                            regNames, funcParNames, ompThreadPrivate, distrStateFromGUI, saveAllLocals,
                            currMessages, countErrors);
                }
            }
            st = st->lexNext();
        }
    }

    //preprocess only module declaration 
    for (auto& mod : modules)
    {
        SgStatement* st = mod->lexNext();
        SgStatement* lastNode = mod->lastNodeOfStmt();
        map<string, vector<SgExpression*>> commonBlocks;
        set<string> privates;
        set<string> deprecatedByIO;
        set<string> funcParNames;

        fillFromModule(st->symbol(), privatesByModule, privates);

        while (st != lastNode)
        {
            if (st->variant() == CONTAINS_STMT)
                break;

            if (!isSPF_stat(st) && !isDVM_stat(st))
            {
                //TODO: set clear regions for modules
                set<ParallelRegion*> currRegs = getAllRegionsByLine(regions, st->fileName(), st->lineNumber());
                vector<string> regNames;
                for (auto& reg : currRegs)
                    regNames.push_back(reg->GetName());
                if (regNames.size() == 0)
                    regNames.push_back("default");

                for (int i = 0; i < 3; ++i)
                    findArrayRefs(st->expr(i), st, "", -1, false, commonBlocks, declaredArrays, declaratedArraysSt, privates, deprecatedByIO,
                        false, "NULL", regNames, funcParNames, ompThreadPrivate, distrStateFromGUI, false,
                        currMessages, countErrors);
            }
            st = st->lexNext();
        }
    }

    //preprocess only block data declaration 
    for (SgStatement* st = file->firstStatement()->lexNext(); st; st = st->lastNodeOfStmt(), st = st->lexNext())
    {
        if (st->variant() == BLOCK_DATA)
        {
            SgStatement* last = st->lastNodeOfStmt();
            SgStatement* curr = st;

            map<string, vector<SgExpression*>> commonBlocks;
            getCommonBlocksRef(commonBlocks, st, last);

            set<string> privates;
            set<string> deprecatedByIO;
            set<string> funcParNames;

            string blockName = "BLOCK DATA";
            if (st->symbol())
                blockName = st->symbol()->identifier();

            while (curr && curr != last)
            {
                //TODO: set clear regions for block data
                set<ParallelRegion*> currRegs = getAllRegionsByLine(regions, curr->fileName(), curr->lineNumber());
                vector<string> regNames;
                for (auto& reg : currRegs)
                    regNames.push_back(reg->GetName());
                if (regNames.size() == 0)
                    regNames.push_back("default");

                for (int i = 0; i < 3; ++i)
                    findArrayRefs(curr->expr(i), curr, "", -1, false, commonBlocks, declaredArrays, declaratedArraysSt, privates, deprecatedByIO,
                        false, blockName, regNames, funcParNames, ompThreadPrivate, distrStateFromGUI, false,
                        currMessages, countErrors);
                curr = curr->lexNext();
            }
        }
    }
    return countErrors;
}
