#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdint>

#include <string>
#include <fstream>
#include <iostream>
#include <algorithm>

#include <vector>
#include <map>
#include <set>
#include <queue>
#include <utility>
#include <assert.h>

#include <thread>
#include <exception>

#ifdef __BOOST
#include <boost/thread.hpp>
#include <boost/chrono.hpp>
#endif

#include "errors.h"
#include "version.h"
#include "get_information.h"
#include "dvm.h"
#include "../Sapfor.h"
#include "graph_loops_func.h"
#include "graph_calls_func.h"
#include "utils.h"
#include "../Sapfor.h"
#include "../ParallelizationRegions/ParRegions.h"
#include "SendMessage.h"
#include "../Predictor/PredictScheme.h"
#include "../DynamicAnalysis/gcov_info.h"
#include "../DynamicAnalysis/gCov_parser_func.h"
#include "../Distribution/CreateDistributionDirs.h"
#include "../LoopAnalyzer/loop_analyzer.h"
#include "../DirectiveProcessing/insert_directive.h"
#include "../ProjectManipulation/PerfAnalyzer.h"
#include "Utils/json.hpp"

#include "BuildGraph.h"

#ifdef _WIN32
#include <direct.h>
#else
#include <unistd.h>
#define _sleep(x) usleep(x * 1000)
#endif

using std::string;
using std::wstring;
using std::map;
using std::set;
using std::vector;
using std::queue;
using std::pair;
using std::tuple;
using std::to_string;
using std::make_pair;
using json = nlohmann::json;

extern bool runAsClient;
extern const char* VISUALIZER_DATA_PATH;

bool showDebug = false;
static const string interruptEx = "Interrupted by user";

static inline int strLen(const short* shString)
{
    int t = 0;
    if (shString == NULL)
        return 0;

    while (shString[t] != '\0')
        t++;
    return t;
}

static char* ConvertShortToChar(const short* source, int& strL)
{
    char* dist = NULL;
    if (source == NULL)
        return dist;

    strL = strLen(source);
    dist = new char[strL + 1];

    for (int i = 0; i < strL; ++i)
        dist[i] = (char)source[i];
    dist[strL] = '\0';

    return dist;
}

extern bool ignoreArrayDistributeState;
static void setOptions(const short* options, bool isBuildParallel = false, const set<int>* turnOffOptions = NULL)
{
    if (!optionNames[STATIC_SHADOW_ANALYSIS])
    {
        optionNames[STATIC_SHADOW_ANALYSIS] = "STATIC_SHADOW_ANALYSIS";
        optionNames[STATIC_PRIVATE_ANALYSIS] = "STATIC_PRIVATE_ANALYSIS";
        optionNames[FREE_FORM] = "FREE_FORM";
        optionNames[KEEP_DVM_DIRECTIVES] = "KEEP_DVM_DIRECTIVES";
        optionNames[KEEP_SPF_DIRECTIVES] = "KEEP_SPF_DIRECTIVES";
        optionNames[PARALLIZE_FREE_LOOPS] = "PARALLIZE_FREE_LOOPS";
        optionNames[MAX_SHADOW_WIDTH] = "MAX_SHADOW_WIDTH";
        optionNames[OUTPUT_UPPER] = "OUTPUT_UPPER";
        optionNames[TRANSLATE_MESSAGES] = "TRANSLATE_MESSAGES";
        optionNames[KEEP_LOOPS_CLOSE_NESTING] = "KEEP_LOOPS_CLOSE_NESTING";
        optionNames[KEEP_GCOV] = "KEEP_GCOV";
        optionNames[ANALYSIS_OPTIONS] = "ANALYSIS_OPTIONS";
        optionNames[DEBUG_PRINT_ON] = "DEBUG_PRINT_ON";
        optionNames[SHARED_MEMORY] = "SHARED_MEMORY";
        optionNames[IGNORE_IO_SAPFOR] = "IGNORE_IO_SAPFOR";
        optionNames[KEEP_SPF_DIRECTIVES_AMONG_TRANSFORMATIONS] = "KEEP_SPF_DIRECTIVES_AMONG_TRANSFORMATIONS";
        optionNames[PARSE_FOR_INLINE] = "PARSE_FOR_INLINE";
        optionNames[EMPTY_OPTION] = "EMPTY_OPTION";
    }

    int len;
    char* conv = ConvertShortToChar(options, len);
    string convS(conv);
    delete []conv;

    vector<string> splited;
    vector<int> intOptions;
    splitString(convS, '|', splited);

    intOptions.resize(EMPTY_OPTION);
    std::fill(intOptions.begin(), intOptions.end(), -1);

    for (int z = STATIC_SHADOW_ANALYSIS; z < EMPTY_OPTION; ++z) //TODO: extend
    {
        if (splited.size() == z)
            break;

        __spf_print(1, "  read value '%s' to '%s' option\n", splited[z].c_str(), optionNames[z]);
        if (z != ANALYSIS_OPTIONS)
        {
            if (sscanf(splited[z].c_str(), "%d", &intOptions[z]) != 1)
            {
                __spf_print(1, "  !wrong value!\n");
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            }
        }
        else
            intOptions[z] = -1;
    }

    //staticShadowAnalysis = intOptions[STATIC_SHADOW_ANALYSIS];
    staticPrivateAnalysis = intOptions[STATIC_PRIVATE_ANALYSIS];
    out_free_form = intOptions[FREE_FORM];
    if (out_free_form == 1)
        out_line_unlimit = 1;

    if (isBuildParallel)
        keepSpfDirs = intOptions[KEEP_SPF_DIRECTIVES];
    else
        keepSpfDirs = intOptions[KEEP_SPF_DIRECTIVES_AMONG_TRANSFORMATIONS];
    maxShadowWidth = intOptions[MAX_SHADOW_WIDTH];
    out_upper_case = intOptions[OUTPUT_UPPER];
    langOfMessages = intOptions[TRANSLATE_MESSAGES];
    removeNestedIntervals = (intOptions[KEEP_LOOPS_CLOSE_NESTING] == 1);
    showDebug = (intOptions[DEBUG_PRINT_ON] == 1);

    sharedMemoryParallelization =         (sharedMemoryParallelization != 1) ? intOptions[SHARED_MEMORY] : sharedMemoryParallelization;
    parallizeFreeLoops = (sharedMemoryParallelization == 1) ? 0 : intOptions[PARALLIZE_FREE_LOOPS];
    ignoreIO =           (sharedMemoryParallelization == 1) ? 1 : intOptions[IGNORE_IO_SAPFOR];
    keepDvmDirectives =  (sharedMemoryParallelization == 1) ? 0 : intOptions[KEEP_DVM_DIRECTIVES];

    parseForInlining = intOptions[PARSE_FOR_INLINE];

    string optAnalisys = splited.size() > ANALYSIS_OPTIONS ? splited[ANALYSIS_OPTIONS] : "";

    if (sharedMemoryParallelization == 1)
        ignoreArrayDistributeState = true;
    else
        ignoreArrayDistributeState = false;

    if (!turnOffOptions)
        return;

    for (auto& elem : *turnOffOptions)
    {
        if (elem == STATIC_SHADOW_ANALYSIS)
            staticShadowAnalysis = 0;
        else if (elem == STATIC_PRIVATE_ANALYSIS)
            staticPrivateAnalysis = 0;
        else if (elem == FREE_FORM)
        {
            out_free_form = 0;
            out_line_unlimit = 0;
        }
        else if (elem == KEEP_DVM_DIRECTIVES)
            keepDvmDirectives = 0;
        else if (elem == KEEP_SPF_DIRECTIVES)
            keepSpfDirs = 0;
        else if (elem == PARALLIZE_FREE_LOOPS)
            parallizeFreeLoops = 0;
        //TODO: to add if needed
    }
}

static bool tryOpenProjectFile(const char *project)
{
    bool ret = true;
    FILE *f = fopen(project, "rb");
    if (f == NULL)
        ret = false;
    else
        fclose(f);
    return ret;
}

volatile int passDone = 0;
static volatile int rethrow = 0;
static void runPassesLoop(const vector<passes> &passesToRun, const char *prName, const char *folderNameChar)
{    
    try
    {
        setPassValues();
        if (showDebug)
            printf("SAPFOR: all passes num %zd\n", passesToRun.size());
        for (int i = 0; i < passesToRun.size(); ++i)
        {
            if (showDebug)
                printf("SAPFOR: run pass %d -> %s\n", passesToRun[i], passNames[passesToRun[i]]);
            __spf_print(1, "SAPFOR: run pass %d -> %s\n", passesToRun[i], passNames[passesToRun[i]]);
            runPass(passesToRun[i], prName, folderNameChar, true);
        }
    }
#if  __BOOST
    catch (boost::thread_interrupted&)
    {
        if (showDebug)
            printf("SAPFOR: thread was terminated\n");
        fflush(NULL);
        return;
    }
#endif
    catch (std::exception& ex)
    {
        printf("SAPFOR: thread was terminated with exception: %s\n", ex.what());
        fflush(NULL);
        if (interruptEx == ex.what())
            rethrow = 1;
        else
            rethrow = -1;
    }
    catch (int ex)
    {
        __spf_print(1, "catch code %d\n", ex);
        rethrow = ex;
    }
    catch (...)
    {
        rethrow = -1;
    }

    if (showDebug)
        printf("SAPFOR: exit with pass == 1\n");
    passDone = 1;
}

static void runPassesForVisualizer(const short *projName, const vector<passes> &passesToRun, const short *folderName = NULL)
{
    int strL = 0, strF = 0;
    char *prName = ConvertShortToChar(projName, strL);
    char *folderNameChar = ConvertShortToChar(folderName, strF);    

    try
    {
        if (strL == 0)
        {
            prName = new char[16];
            sprintf(prName, "dvm.proj");
        }
        
        if (tryOpenProjectFile(prName) == false)
        {
            __spf_print(1, "Can not open project '%s'\n", prName);
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
        }
             
        passDone = 0;
        rethrow = 0;

        std::thread thread { runPassesLoop, passesToRun, prName, folderNameChar };

        int timeToWait = 10;
        int steps = 0;
        while (passDone == 0)
        {
            FILE* interrupt_old = fopen("INTERRUPT", "r");
            FILE* interrupt = fopen((string(VISUALIZER_DATA_PATH) + "/INTERRUPT").c_str(), "r");
            if (interrupt || interrupt_old)
            {
                if (showDebug)
                    printf("[SAPFOR]: file exists, start interruption\n");
                fflush(NULL);

                if (interrupt_old)
                    fclose(interrupt_old);
                if (interrupt)
                    fclose(interrupt);
                passDone = 2;
            }

            if (showDebug)
            {
                if (runAsClient)
                    printf("[SAPFOR]: ");
                printf("wait %d\n", timeToWait);
            }
            _sleep(timeToWait);
            
            if (steps > 4)
            {
                if (timeToWait < 1000)
                    timeToWait *= 2;
                steps = 0;
            }
            else 
                steps++;
        }
        if (showDebug)
            printf("[SAPFOR]: start wait thread join, pass == %d\n", passDone);
        thread.join();
        if (showDebug)
            printf("[SAPFOR]: end wait thread join\n");

        if (passDone == 2)
            rethrow = 1;
    }
    catch (...)
    {
        rethrow = -1;
    }

    if (prName)
        delete []prName;
    if (folderNameChar)
        delete []folderNameChar;
    
    if (rethrow == 1)
    {
        if (showDebug)
            printf("SAPFOR: rethrow -99\n");
        fflush(NULL);
        throw -99;
    }
    else if (rethrow != 0)
        throw rethrow;
}

int SPF_StatisticAnalyzer(void*& context, int winHandler, short* options, short* pppaOptions, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    clearGlobalMessagesBuffer();
    setOptions(options);

    int tmp;
    char* optionsStr = ConvertShortToChar(pppaOptions, tmp);

    int retSize = -1;
    try
    {
        if (pppaAnalyzer(optionsStr) != 0)
            retSize = 1;
        else
            retSize = 0;
    }
    catch (int ex)
    {
        try { __spf_print(1, "catch code %d\n", ex); }
        catch (...) { }

        if (ex == -99)
            return -99;
        else
            retSize = ex;
    }
    catch (...)
    {
        retSize = -1;
    }
    
    convertBuffers(outputMessage, output);

    if (showDebug)
        printf("SAPFOR: return from DLL\n");
    MessageManager::setWinHandler(-1);
    return retSize;
}

int SPF_ParseFiles(void*& context, int winHandler, short *options, short* projName, string& output, string& outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    clearGlobalMessagesBuffer();
    setOptions(options);

    int retSize = -1;
    try
    {
        runPassesForVisualizer(projName, { PARSE_FILES });
        retSize = 0;
    }
    catch (int ex)
    {
        try { __spf_print(1, "catch code %d\n", ex); }
        catch (...) {}
        if (ex == -99)
            return -99;
        else
            retSize = ex;
    }
    catch (...)
    {
        retSize = -1;
    }
    
    convertBuffers(outputMessage, output);

    if (showDebug)
        printf("SAPFOR: return from DLL\n");
    MessageManager::setWinHandler(-1);
    return retSize;
}

extern vector<string> filesCompilationOrder;
int SPF_ParseFilesWithOrder(void*& context, int winHandler, short* options, short* projName, string& result, string& output, string& outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    clearGlobalMessagesBuffer();
    setOptions(options);

    int retSize = -1;
    try
    {
        runPassesForVisualizer(projName, { PARSE_FILES });

        json filesArray = json::array();
        
        for (auto& file : filesCompilationOrder)
            filesArray.push_back(file);            
        
        json filesOrder;
        filesOrder["allFiles"] = filesArray;

        result = filesOrder.dump();        
        retSize = 0;
    }
    catch (int ex)
    {
        try { __spf_print(1, "catch code %d\n", ex); }
        catch (...) {}
        if (ex == -99)
            return -99;
        else
            retSize = ex;
    }
    catch (...)
    {
        retSize = -1;
    }
    
    convertBuffers(outputMessage, output);

    if (showDebug)
        printf("SAPFOR: return from DLL\n");
    MessageManager::setWinHandler(-1);
    return retSize;
}

extern map<string, vector<LoopGraph*>> loopGraph; // file -> Info
int SPF_GetGraphLoops(void*& context, int winHandler, short *options, short *projName, string& result, string& output, string& outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    clearGlobalMessagesBuffer();
    setOptions(options);

    int retSize = -1;
    try
    {
        runPassesForVisualizer(projName, { LOOP_GRAPH } );

        result = convertToJson(loopGraph).dump();        
        retSize = result.size();
    }
    catch (int ex)
    {
        try { __spf_print(1, "catch code %d\n", ex); } catch (...) { }
        if (ex == -99)
            return -99;
        else
            retSize = -1;
    }
    catch (...)
    {
        retSize = -1;
    }
        
    convertBuffers(outputMessage, output);

    if (showDebug)
        printf("SAPFOR: return from DLL\n");

    MessageManager::setWinHandler(-1);
    return retSize;
}

extern map<string, vector<FuncInfo*>> allFuncInfo; // file -> Info  
int SPF_GetGraphFunctions(void*& context, int winHandler, short *options, short *projName, string& result, string& output, string& outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    clearGlobalMessagesBuffer();
    setOptions(options);

    int retSize = -1;    
    try
    {
        runPassesForVisualizer(projName, { FILL_PAR_REGIONS_LINES } );
         
        result = convertToJson(allFuncInfo).dump();        
        retSize = result.size();
    }
    catch (int ex)
    {
        try { __spf_print(1, "catch code %d\n", ex); } catch (...) { }
        if (ex == -99)
            return -99;
        else
            retSize = -1;   
    }
    catch (...)
    {
        retSize = -1;
    }
        
    convertBuffers(outputMessage, output);

    if (showDebug)
        printf("SAPFOR: return from DLL\n");

    MessageManager::setWinHandler(-1);
    return retSize;
}

int SPF_GetGraphFunctionPositions(void*& context, int winHandler, short *options, short *projName, string& result, string& output, string& outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    clearGlobalMessagesBuffer();
    setOptions(options);

    int retSize = -1;    
    try
    {
        runPassesForVisualizer(projName, { FILL_PAR_REGIONS_LINES } );

        int len;
        char* conv = ConvertShortToChar(options, len);
        string convS(conv);
        delete[]conv;

        vector<string> splited;
        splitString(convS, '|', splited);
        int iters = 1000;
        int coef = 100;
        int w = 1920; 
        int h = 1080;
        int opts[4];

        bool sendVisible = false;
        set<string> visible;
        if (splited.size() > EMPTY_OPTION + 3)
        {
            for (int z = 0; z < 4; ++z)
            {
                if (sscanf(splited[EMPTY_OPTION + z].c_str(), "%d", &opts[z]) != 1)
                {
                    __spf_print(1, "!wrong value!\n");
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                }
            }
            iters = opts[0]; coef = opts[1]; w = opts[2]; h = opts[3];
            __spf_print(1, "set iters=>%d, coef=>%d, w=>%d, h=>%d\n", iters, coef, w, h);

            sendVisible = splited.size() >= (EMPTY_OPTION + 1) + 4;
            if (sendVisible)
            {
                int viz;
                if (sscanf(splited[EMPTY_OPTION + 4].c_str(), "%d", &viz) != 1)
                {
                    __spf_print(1, "!wrong value!\n");
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                }

                for (int z = EMPTY_OPTION + 5; z < EMPTY_OPTION + 5 + viz; ++z)
                    visible.insert(splited[z]);

                if (viz != visible.size())
                {
                    __spf_print(1, "!num visible not equal!\n");
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                }
            }
        }

        auto positions = buildLocationOfGraph(allFuncInfo, iters, coef, 0.9 * w, 0.9 * h, sendVisible ? &visible : NULL);
        
        json pos_array= json::array();
        for (auto& [fname, coords] : positions) {
            json elem;
            elem["functionName"] = fname;
            elem["x"] = (double)coords.first;
            elem["y"] = (double)coords.second;
            pos_array.push_back(elem);
        }

        json allPositions;
        allPositions["allPositions"] = pos_array;

        result = allPositions.dump();        
        retSize = result.size();
    }
    catch (int ex)
    {
        try { __spf_print(1, "catch code %d\n", ex); } catch (...) { }
        if (ex == -99)
            return -99;
        else
            retSize = -1;   
    }
    catch (...)
    {
        retSize = -1;
    }
        
    convertBuffers(outputMessage, output);

    if (showDebug)
        printf("SAPFOR: return from DLL\n");

    MessageManager::setWinHandler(-1);
    return retSize;
}

extern int PASSES_DONE[EMPTY_PASS];
extern int *ALGORITHMS_DONE[EMPTY_ALGO];
extern const char *passNames[EMPTY_PASS + 1];

int SPF_GetPassesState(void*& context, int *&passInfo)
{
    MessageManager::clearCache();
    passInfo = PASSES_DONE;
    return EMPTY_PASS;
}

int SPF_GetPassesStateStr(void*& context, string &passInfo)
{
    MessageManager::clearCache();
    passInfo = "";
    for (int i = 0; i < EMPTY_PASS; ++i)
    {
        if (showDebug)
            printf("SAPFOR: pass %d is %d with name %s\n", 1, PASSES_DONE[i], passNames[i]);
        if (PASSES_DONE[i] == 1)
        {
            passInfo += passNames[i] + string("|");
        }
    }

    //erase last "|"
    if (passInfo != "" && passInfo[passInfo.size() - 1] == '|')
        passInfo.erase(passInfo.end() - 1);

    return passInfo.size();
}


extern map<tuple<int, string, string>, pair<DIST::Array*, DIST::ArrayAccessInfo*>> declaredArrays;
static void printDeclArraysState()
{
    if (showDebug)
        printf("SAPFOR: decl state: \n");
    int dist = 0, priv = 0, err = 0;
    for (auto it = declaredArrays.begin(); it != declaredArrays.end(); ++it)
    {
        if (it->second.first->IsNotDistribute() == false)
            //printf("array '%s' is DISTR\n", it->second.first->GetShortName().c_str());
            dist++;
        else if (it->second.first->IsNotDistribute() == true)
            //printf("array '%s' is PRIVATE\n", it->second.first->GetShortName().c_str());
            priv++;
        else
            //printf("array '%s' is ERROR\n", it->second.first->GetShortName().c_str());
            err++;        
    }
    if (showDebug)
        printf("   PRIV %d, DIST %d, ERR %d, ALL %d\n", priv, dist, err, dist + priv + err);
}

extern vector<ParallelRegion*> parallelRegions;
int SPF_GetArrayDistribution(void*& context, int winHandler, short *options, short *projName, string& result, string& output, string& outputMessage,
                             int regime)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    printDeclArraysState();
    clearGlobalMessagesBuffer();
    setOptions(options);

    int retSize = -1;
    try
    {
        if (regime == 0)
            runPassesForVisualizer(projName, { CREATE_TEMPLATE_LINKS });
        else if (regime == 1)
        {
            if (sharedMemoryParallelization)
            {
                ignoreArrayDistributeState = true;
                runPassesForVisualizer(projName, { LOOP_ANALYZER_NODIST });
            }
            else
                runPassesForVisualizer(projName, { LOOP_ANALYZER_DATA_DIST_S1 });
        }
        else if (regime == 2)
            runPassesForVisualizer(projName, { FILL_PAR_REGIONS });
        else
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);            

        json regions = json::array();
        for (auto& reg : parallelRegions)
        {
            json currReg = reg->toJson();
            regions.push_back(currReg);
        }

        json allRegions;
        allRegions["allRegions"] = regions;

        result = allRegions.dump();
        retSize = result.size();
    }
    catch (int ex)
    {
        if (ex == -99)
            return -99;
        else
            retSize = -1;
    }
    catch (...)
    {
        retSize = -1;
    }

    convertBuffers(outputMessage, output);

    if (showDebug)
        printf("SAPFOR: return from DLL with code %d\n", retSize);
    MessageManager::setWinHandler(-1);
    return retSize;
}

extern map<string, vector<Messages>> SPF_messages;
extern map<DIST::Array*, set<DIST::Array*>> arrayLinksByFuncCalls;

//toModify[0] = size, toModify[1] arrayAddr, all triplets to modify for each dims
//ex: toModify A[1*J + 1]: [0] = 4, [1] = x000A, [2] = 0, [3] = 1, [4] = 1
int SPF_ModifyArrayDistribution(void*& context, int winHandler, short *options, short *projName, string &output, string &outputMessage,
                                int regId, int64_t *toModify)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    printDeclArraysState();
    clearGlobalMessagesBuffer();
    setOptions(options);

    int retSize = 0;
    try
    {
        if (toModify == NULL || regId < 0)
            throw (-22);

        ParallelRegion *reg = NULL;
        for (int k = 0; k < parallelRegions.size(); ++k)
        {
            if (regId == parallelRegions[k]->GetId())
            {
                reg = parallelRegions[k];
                break;
            }
        }

        if (reg == NULL)
            throw (-23);

        map<DIST::Array*, vector<pair<int, int>>> data;
        auto arrays = reg->GetAllArrays().GetArrays();
        set<DIST::Array*> realRefsArrays;
        for (auto& elem : arrays)
        {
            if (elem->IsArray())
            {
                set<DIST::Array*> realRefs;
                getRealArrayRefs(elem, elem, realRefs, arrayLinksByFuncCalls);
                realRefsArrays.insert(realRefs.begin(), realRefs.end());
            }
        }

        int countOfArray = realRefsArrays.size();

        int z = 1;
        int last = *toModify;

        set<DIST::Array*> templates;
        for ( ; z < last; ++z)
        {
            int64_t addr = toModify[z];
            auto it = arrays.find((DIST::Array*)addr);
            if (it == arrays.end())
                throw (-24);

            DIST::Array* array = *it;
            DIST::Array* templ = array->GetTemplateArray(regId);
            if (!templ || !templ->IsTemplate())
                throw (-30);
            templates.insert(templ);

            if (array->IsLoopArray())
                throw (-26);
            if (array->IsTemplate())
                throw (-27);

            auto it2 = data.find(array);
            if (it2 != data.end())
                throw (-25);

            it2 = data.insert(it2, make_pair(array, vector<pair<int, int>>()));
            it2->second.resize(array->GetDimSize());
            std::fill(it2->second.begin(), it2->second.end(), make_pair(0, 0));

            ++z;
            int k = z;
            for ( ; k < z + 3 * array->GetDimSize(); k += 3)
                if (toModify[k] != -1)
                    it2->second[toModify[k]] = make_pair((int)toModify[k + 1], (int)toModify[k + 2]);
            z = k - 1;
        }
        if (z != last)
            throw (-28);

        DIST::GraphCSR<int, double, attrType> &reducedG = reg->GetReducedGraphToModify();
        DIST::Arrays<int> &allArrays = reg->GetAllArraysToModify();
        DataDirective &dataDirectives = reg->GetDataDirToModify();

        if (countOfArray != data.size())
            throw (-29);

        // recalculate links
        reducedG.cleanCacheLinks();
        dataDirectives.alignRules.clear();

        map<DIST::Array*, vector<pair<int, int>>> toDelete;
        toDelete = data;
        for (auto& elem : templates)
        {
            toDelete.insert(make_pair(elem, vector<pair<int, int>>()));
            elem->SetDimSizesToMaxMin(true);
        }
        reducedG.RemoveAllEdgesFromGraph(toDelete, allArrays);

        // add new rules and modify reduced graph
        for (auto& arrayP : data)
        {
            DIST::Array* array = arrayP.first;
            auto links = array->GetLinksWithTemplate(regId);
            auto templ = array->GetTemplateArray(regId);
            if (!templ)
                throw (-32);

            for (int z = 0; z < links.size(); ++z)
            {
                if (links[z] != -1)
                {
                    array->AddLinkWithTemplate(z, links[z], templ, arrayP.second[z], regId);
                    int err = DIST::AddArrayAccess(reducedG, allArrays, array, templ, make_pair(z, links[z]), 1.0, make_pair(make_pair(1, 0), arrayP.second[z]), WW_link);
                    if (err != 0)
                        throw (-33);
                }
                else
                {
                    if (arrayP.second[z].first != 0)
                        throw (-31);                    
                }
            }
        }

        createAlignDirs(reducedG, allArrays, dataDirectives, reg->GetId(), arrayLinksByFuncCalls, SPF_messages);

        __spf_print(1, "*** NEW RULES FOR PARALLEL REGION '%s':\n", reg->GetName().c_str());
        auto result = dataDirectives.GenAlignsRules();

        for (int i = 0; i < result.size(); ++i)
            __spf_print(1, "  %s\n", result[i].c_str());

        // clear user directives from all loops
        for (auto& byFile : loopGraph)
        {
            if (SgFile::switchToFile(byFile.first) == -1)
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            for (auto& loop : byFile.second)
                loop->clearUserDirectives();
        }

        // clear other dirs
        set<int> varsToClear = { ACC_REGION_DIR, ACC_END_REGION_DIR, ACC_ACTUAL_DIR, ACC_GET_ACTUAL_DIR, 
                                 DVM_SHADOW_DIR, DVM_REALIGN_DIR, DVM_REDISTRIBUTE_DIR, DVM_INHERIT_DIR, DVM_DYNAMIC_DIR };
        removeStatementsFromAllproject(varsToClear);
    }
    catch (int ex)
    {
        if (ex == -99)
            return -99;
        else
            retSize = -1;
    }
    catch (...)
    {
        retSize = -1;
    }

    if (showDebug)
        printf("SAPFOR: return from DLL with code %d\n", retSize);
    MessageManager::setWinHandler(-1);
    return retSize;
}

extern map<string, PredictorStats> allPredictorStats;
extern map<string, vector<SpfInterval*>> intervals;
extern vector<vector<long>> topologies;

int SPF_CreateParallelVariant(void*& context, int winHandler, short *options, short *projName, short *folderName, int64_t *variants, int *varLen,
                              string &output, string &outputMessage, string &predictorStats)
{
    MessageManager::clearCache();
    if (folderName == NULL)
        MessageManager::setWinHandler(-1);
    else
        MessageManager::setWinHandler(winHandler);

    allPredictorStats.clear();
    clearGlobalMessagesBuffer();
    setOptions(options, true);

    int retSize = 0;
    try
    {
        if (varLen == NULL || variants == NULL)
            throw (-2);
        if ((*varLen % 3) != 0)
            throw (-5);

        int countOfDist = 0;
        if (sharedMemoryParallelization == 0)
        {
            map<uint64_t, vector<pair<int64_t, int64_t>>> varLens;
            for (int i = 0, k = 0; i < *varLen; i += 3, ++k)
            {
                if (showDebug)
                    printf("SAPFOR: input pack %d: %lld %lld %lld\n", k, variants[i], variants[i + 1], variants[i + 2]);
                varLens[variants[i + 2]].push_back(make_pair(variants[i], variants[i + 1]));
            }

            if (varLens.size() != parallelRegions.size())
                throw (-6);

            for (int z = 0; z < parallelRegions.size(); ++z)
            {
                auto it = varLens.find(parallelRegions[z]->GetId());
                if (it == varLens.end())
                    throw (-7);

                const vector<pair<int64_t, int64_t>>& currVars = it->second;
                const DataDirective& dataDirectives = parallelRegions[z]->GetDataDir();

                vector<int> currentVariant(dataDirectives.distrRules.size());
                if (currVars.size() != dataDirectives.distrRules.size())
                {
                    if (showDebug)
                        printf("SAPFOR: currV %d, dataDirectives.distrRules %d\n", (int)currVars.size(), (int)dataDirectives.distrRules.size());
                    throw (-3);
                }

                map<int64_t, int> varMap;
                for (int i = 0; i < currVars.size(); ++i)
                    varMap[currVars[i].first] = (int)currVars[i].second;

                map<int64_t, int> templateIdx;
                for (int i = 0; i < dataDirectives.distrRules.size(); ++i)
                {
                    if (showDebug)
                        printf("SAPFOR: template address %lld with num %d\n", (int64_t)dataDirectives.distrRules[i].first, i);
                    templateIdx[(int64_t)dataDirectives.distrRules[i].first] = i;
                }

                for (auto it = varMap.begin(); it != varMap.end(); ++it)
                {
                    auto itF = templateIdx.find(it->first);
                    if (itF == templateIdx.end())
                        throw (-4);

                    if (showDebug)
                        printf("SAPFOR: found %lld address\n", it->first);
                    currentVariant[itF->second] = it->second;

                    for (auto& elem : dataDirectives.distrRules[itF->second].second[it->second].distRule)
                    {
                        if (elem == BLOCK)
                            countOfDist++;
                    }
                }
                parallelRegions[z]->SetCurrentVariant(currentVariant);
            }
        }
        if (showDebug)
            printf("SAPFOR: set all info done\n");
        runPassesForVisualizer(projName, { INSERT_PARALLEL_DIRS }, folderName);

        PredictorStats summed;
        for (auto &predFile : allPredictorStats)
        {
            summed.IntervalCount += predFile.second.IntervalCount;
            summed.ParallelCount += predFile.second.ParallelCount;
            summed.RedistributeCount += predFile.second.RedistributeCount;
            summed.RemoteCount += predFile.second.RemoteCount;
            summed.ParallelStat.AcrossCount += predFile.second.ParallelStat.AcrossCount;
            summed.ParallelStat.ReductionCount += predFile.second.ParallelStat.ReductionCount;
            summed.ParallelStat.RemoteCount += predFile.second.ParallelStat.RemoteCount;
            summed.ParallelStat.ShadowCount += predFile.second.ParallelStat.ShadowCount;

            summed.TotalScoreComm += predFile.second.TotalScoreComm;
            summed.TotalScoreDist += predFile.second.TotalScoreDist;
            summed.TotalScorePar += predFile.second.TotalScorePar;
        }

        predictorStats = summed.toJson().dump();
        __spf_print(1, "  statistic to send: %s\n", predictorStats.c_str());
        retSize = predictorStats.size();
    }
    catch (int ex)
    {
        try { __spf_print(1, "catch code %d\n", ex); } catch (...) { }
        if (ex == -99)
            return -99;
        else
            retSize = -1;
        retSize = ex;
    }
    catch (...)
    {
        retSize = -1;
    }

    convertBuffers(outputMessage, output);

    if (showDebug)
        printf("SAPFOR: return from DLL\n");
    MessageManager::setWinHandler(-1);
    return retSize;
}


int SPF_GetArrayLinks(void*& context, int winHandler, short *options, short *projName, string &result, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    clearGlobalMessagesBuffer();
    setOptions(options);

    int retSize = -1;    
    try
    {
        runPassesForVisualizer(projName, { CALL_GRAPH2 } );

        map<DIST::Array*, set<DIST::Array*>> linkedArrays;
        for (auto& inMap : arrayLinksByFuncCalls)
        {
            set<DIST::Array*> refs;
            getRealArrayRefs(inMap.first, inMap.first, refs, arrayLinksByFuncCalls);

            if (refs.size() > 1)
                for (auto& ref : refs)
                    for (auto& toAdd : refs)
                        if (ref != toAdd)
                            linkedArrays[ref].insert(toAdd);
        }

        json allLinks = json::array();
        for (auto& array : linkedArrays)
        {
            json currLink;
            currLink["id"] = array.first->GetId();

            json links = json::array();
            for (auto& link : array.second)
                links.push_back(link->GetId());
            currLink["links"] = links;

            allLinks.push_back(currLink);
        }

        json links;
        links["allLinks"] = allLinks;

        result = links.dump();
        retSize = result.size();
    }
    catch (int ex)
    {
        try { __spf_print(1, "catch code %d\n", ex); } catch (...) { }
        if (ex == -99)
            return -99;
        else
            retSize = -1;
    }
    catch (...)
    {
        retSize = -1;
    }
        
    convertBuffers(outputMessage, output);

    if (showDebug)
        printf("SAPFOR: return from DLL\n");

    MessageManager::setWinHandler(-1);
    return retSize;
}

extern std::pair<int, int> min_max_block;
int SPF_GetMaxMinBlockDistribution(void*& context, int winHandler, short *options, short *projName, string &result, string &output,
                                   string &outputMessage)
{
    MessageManager::clearCache();
    //MessageManager::setWinHandler(winHandler);
    clearGlobalMessagesBuffer();
    setOptions(options);

    int retSize = -1;    
    try
    {
        runPassesForVisualizer(projName, { GET_MIN_MAX_BLOCK_DIST });

        result = to_string(min_max_block.first) + " " + to_string(min_max_block.second);
        retSize = result.size();
    }
    catch (int ex)
    {
        try { __spf_print(1, "catch code %d\n", ex); } catch (...) { }
        if (ex == -99)
            return -99;
        else
            retSize = -1;
    }
    catch (...)
    {
        retSize = -1;
    }
        
    convertBuffers(outputMessage, output);

    if (showDebug)
        printf("SAPFOR: return from DLL\n");

    MessageManager::setWinHandler(-1);
    return retSize;
}

int SPF_GetVersionAndBuildDate(void*& context, string &result)
{
    MessageManager::clearCache();
    json info;

    info["version"] = VERSION_SPF;
    info["build_date"] = __DATE__;
    info["build_time"] = __TIME__;
    
    result = info.dump();
    return result.size();
}

int SPF_GetCurrentPID(void*& context, string &result)
{
    MessageManager::clearCache();
    result = to_string(GetPid());
    return result.size();
}

extern string openDvmStatistic(const char* path, bool& isOk);
int SPF_OpenDvmStatistic(void*& context, const short* path, string &result)
{
    MessageManager::clearCache();
    result = "";

    int strL = -1;
    char* pathCh = ConvertShortToChar(path, strL);
    checkNull(pathCh, convertFileName(__FILE__).c_str(), __LINE__);

    bool ok = true;
    result = openDvmStatistic(pathCh, ok);
    if (!ok)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    return result.size();
}

extern set<string> intrinsicF;
extern void initIntrinsicFunctionNames();

int SPF_GetIntrinsics(void*& context, string &result)
{
    MessageManager::clearCache();
    initIntrinsicFunctionNames();

    result = "";
    
    for (auto it = intrinsicF.begin(); it != intrinsicF.end(); ++it)
    {
        if (it != intrinsicF.begin())
            result += " " + *it;
        else
            result += *it;
    }
    return result.size();
}

extern map<string, map<int, set<string>>> includeDependencies;
int SPF_GetIncludeDependencies(void*& context, int winHandler, short *options, short *projName, string &result, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);

    setOptions(options);
    int retSize = 0;
    try
    {
        runPassesForVisualizer(projName, { BUILD_INCLUDE_DEPENDENCIES });

        json inc_array = json::array();
        for (const auto& deps : includeDependencies)
        {
            json includes;
            includes["file"] = deps.first;

            json array = json::array();
            for (const auto& [line, incls] : deps.second)
            {
                for (auto& incl : incls)
                {
                    json elem;
                    elem["line"] = line;
                    elem["dependencyFileName"] = incl;
                    array.push_back(elem);
                }
            }
            includes["includes"] = array;
            inc_array.push_back(includes);
        }

        json allIncludes;
        allIncludes["allIncludes"] = inc_array;
        result = allIncludes.dump();
        retSize = result.size();
    }
    catch (int ex)
    {
        try { __spf_print(1, "catch code %d\n", ex); } catch (...) { }
        if (ex == -99)
            return -99;
        else
            retSize = -1;
    }
    catch (...)
    {
        retSize = -1;
    }

    convertBuffers(outputMessage, output);

    MessageManager::setWinHandler(-1);
    return retSize;
}

int SPF_SetFunctionsToInclude(void*& context, int winHandler, short *options, short *projName, string &result, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    clearGlobalMessagesBuffer();
    setOptions(options);

    int retSize = -1;
    try
    {
        runPassesForVisualizer(projName, { FIND_FUNC_TO_INCLUDE });

        result = convertToJson(allFuncInfo).dump();
        retSize = result.size();
    }
    catch (int ex)
    {
        try { __spf_print(1, "catch code %d\n", ex); } catch (...) { }
        if (ex == -99)
            return -99;
        else
            retSize = -1;
    }
    catch (...)
    {
        retSize = -1;
    }

    convertBuffers(outputMessage, output);

    if (showDebug)
        printf("SAPFOR: return from DLL\n");
    MessageManager::setWinHandler(-1);
    return retSize;
}

int SPF_GetAllDeclaratedArrays(void*& context, int winHandler, short *options, short *projName, string &result, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    clearGlobalMessagesBuffer();
    setOptions(options);

    int retSize = -1;
    try
    {
        runPassesForVisualizer(projName, { GET_ALL_ARRAY_DECL });

        json arrays = json::array();
        for (const auto& [_, array] : declaredArrays)
        {
            json jArray = array.first->toJson();
            arrays.push_back(jArray);
        }

        json allArrays;
        allArrays["allArrays"] = arrays;
        result = allArrays.dump();
        retSize = result.size();
    }
    catch (int ex)
    {
        try { __spf_print(1, "catch code %d\n", ex); } catch(...) { }

        if (ex == -99)
            return -99;
        else
            retSize = -1;
    }
    catch (...)
    {
        retSize = -1;
    }

    convertBuffers(outputMessage, output);

    if (showDebug)
        printf("SAPFOR: return from DLL\n");
    MessageManager::setWinHandler(-1);
    return retSize;
}

extern map<string, int> lineInfo;
extern map<string, pair<set<int>, set<int>>> dirsInfo;
int SPF_GetFileLineInfo(void*& context, int winHandler, short *options, short *projName, string &result, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    clearGlobalMessagesBuffer();
    setOptions(options);

    int retSize = -1;
    try
    {
        runPassesForVisualizer(projName, { FILE_LINE_INFO });

        json metric_array = json::array();
        for (const auto& fileInfo : lineInfo)
        {
            json fileMetric;

            fileMetric["file"] = fileInfo.first;
            fileMetric["linesCount"] = fileInfo.second;
            
            auto it = dirsInfo.find(fileInfo.first);
            if (it == dirsInfo.end())
            {
                fileMetric["numSPF"] = 0;
                fileMetric["numDVM"] = 0;
            }
            else
            {
                fileMetric["numSPF"] = (int)it->second.first.size();
                fileMetric["numDVM"] = (int)it->second.second.size();
            }

            metric_array.push_back(fileMetric);
        }

        json allMetrics;
        allMetrics["allMetrics"] = metric_array;
        result = allMetrics.dump();        
        retSize = result.size();
    }
    catch (int ex)
    {
        try { __spf_print(1, "catch code %d\n", ex); } catch (...) { }
        if (ex == -99)
            return -99;
        else
            retSize = -1;
    }
    catch (...)
    {
        retSize = -1;
    }

    convertBuffers(outputMessage, output);

    if (showDebug)
        printf("SAPFOR: return from DLL\n");
    MessageManager::setWinHandler(-1);
    return retSize;
}

extern map<string, int> distrStateFromGUI;
int SPF_SetDistributionFlagToArray(void*& context, char *key, int flag)
{
    MessageManager::clearCache();

    if (flag != DIST::DISTR && flag != DIST::NO_DISTR)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    string keyStr(key);
    try
    {
        if (declaredArrays.size())
        {
            for (auto& array : declaredArrays)
            {
                const auto key = array.second.first->GetName();
                if (key == keyStr)
                {
                    __spf_print(1, "change flag for array '%s': %d -> %d\n", key.c_str(), array.second.first->IsNotDistribute(), flag);                    

                    if (flag == DIST::DISTR)
                        array.second.first->SetDistributeFlag(DIST::DISTR);
                    else
                        array.second.first->SetDistributeFlag(DIST::NO_DISTR);
                    break;
                }
            }
        }
        else
            distrStateFromGUI[keyStr] = flag;
    }
    catch (...)
    {
        return -1;
    }

    return 0;
}

int SPF_SetDistributionFlagToArrays(void*& context, const char* keys, const char* flags)
{
    MessageManager::clearCache();

    if (!keys || !flags)
        return 0;

    try
    {
        vector<string> keysS;
        vector<string> flagsS;

        splitString(keys, '|', keysS);
        splitString(flags, '|', flagsS);
        
        if (keysS.size() != flagsS.size())
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        map<string, DIST::Array*> allArrays;
        for (auto& array : declaredArrays)
            allArrays[array.second.first->GetIndepUniqName()] = array.second.first;

        if (allArrays.size())
        {
            for (int z = 0; z < keysS.size(); ++z)
            {
                auto it = allArrays.find(keysS[z]);
                if (it == allArrays.end())
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                int flag = -1;
                if (sscanf(flagsS[z].c_str(), "%d", &flag) != 1)
                {
                    __spf_print(1, "!wrong value!\n");
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                }

                if (flag != DIST::DISTR && flag != DIST::NO_DISTR)
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                __spf_print(1, "change flag for array '%s': %d -> %d\n", keysS[z].c_str(), it->second->IsNotDistribute(), flag);

                if (flag == DIST::DISTR)
                    it->second->SetDistributeFlag(DIST::DISTR);
                else
                    it->second->SetDistributeFlag(DIST::NO_DISTR);
            }
        }
        else
        {
            for (int z = 0; z < keysS.size(); ++z)
            {
                int flagI = -1;
                if (sscanf(flagsS[z].c_str(), "%d", &flagI) == -1)
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                distrStateFromGUI[keysS[z]] = flagI;
            }
        }
    }
    catch (...)
    {
        return -1;
    }
    return 0;
}

static int simpleTransformPass(const passes PASS_NAME, short *options, short *projName, short *folderName,
                               string &output, string &outputMessage, bool isBuildParallel = false)
{
    clearGlobalMessagesBuffer();
    setOptions(options, isBuildParallel);

    int retCode = 0;
    try
    {
        PASSES_DONE[PASS_NAME] = 0;
        runPassesForVisualizer(projName, { PASS_NAME }, folderName);
    }
    catch (int ex)
    {
        __spf_print(1, "catch code %d\n", ex);
        if (ex == -99)
            return -99;
        else
            retCode = -1;
    }
    catch (...)
    {
        retCode = -1;
    }

    convertBuffers(outputMessage, output);

    if (showDebug)
        printf("SAPFOR: return from DLL\n");
    MessageManager::setWinHandler(-1);
    return retCode;
}


int SPF_CorrectCodeStylePass(void*& context, int winHandler, short *options, short *projName, short *folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(CORRECT_CODE_STYLE, options, projName, folderName, output, outputMessage);
}

int SPF_DuplicateFunctionChains(void*& context, int winHandler, short *options, short* projName, short* folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(DUPLICATE_FUNCTIONS, options, projName, folderName, output, outputMessage);
}

int SPF_ResolveParallelRegionConflicts(void*& context, int winHandler, short *options, short *projName, short *folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(RESOLVE_PAR_REGIONS, options, projName, folderName, output, outputMessage);
}

int SPF_RemoveDistArraysFromIO(void*& context, int winHandler, short* options, short* projName, short* folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(REMOVE_DIST_ARRAYS_FROM_IO, options, projName, folderName, output, outputMessage);

}
int SPF_PrivateExpansion(void*& context, int winHandler, short *options, short *projName, short *folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(PRIVATE_ARRAYS_EXPANSION, options, projName, folderName, output, outputMessage);
}
int SPF_PrivateShrinking(void*& context, int winHandler, short *options, short *projName, short *folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(PRIVATE_ARRAYS_SHRINKING, options, projName, folderName, output, outputMessage);
}

int SPF_PrivateRemoving(void*& context, int winHandler, short *options, short *projName, short *folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(PRIVATE_REMOVING, options, projName, folderName, output, outputMessage);
}

int SPF_LoopFission(void*& context, int winHandler, short *options, short *projName, short *folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(LOOPS_SPLITTER, options, projName, folderName, output, outputMessage);
}

int SPF_LoopUnion(void*& context, int winHandler, short *options, short *projName, short *folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(LOOPS_COMBINER, options, projName, folderName, output, outputMessage);
}

int SPF_LoopUnrolling(void*& context, int winHandler, short *options, short *projName, short *folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(UNROLL_LOOPS, options, projName, folderName, output, outputMessage);
}

int SPF_CreateIntervalsTree(void*& context, int winHandler, short *options, short *projName, short *folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(INSERT_INTER_TREE, options, projName, folderName, output, outputMessage);
}

int SPF_RemoveOmpDirectives(void*& context, int winHandler, short* options, short* projName, short* folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(REMOVE_OMP_DIRS_TRANSFORM, options, projName, folderName, output, outputMessage);
}

int SPF_RemoveDvmDirectives(void*& context, int winHandler, short *options, short *projName, short *folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(REMOVE_DVM_DIRS, options, projName, folderName, output, outputMessage);
}

int SPF_RemoveSpfDirectives(void*& context, int winHandler, short* options, short* projName, short* folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(REMOVE_SPF_DIRS, options, projName, folderName, output, outputMessage);
}

int SPF_RemoveDvmDirectivesToComments(void*& context, int winHandler, short *options, short *projName, short *folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(REMOVE_DVM_DIRS_TO_COMMENTS, options, projName, folderName, output, outputMessage);
}

int SPF_RemoveComments(void*& context, int winHandler, short* options, short* projName, short* folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(REMOVE_COMMENTS, options, projName, folderName, output, outputMessage);
}

int SPF_RemoveDvmIntervals(void*& context, int winHandler, short *options, short *projName, short *folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(REMOVE_DVM_INTERVALS, options, projName, folderName, output, outputMessage);
}

int SPF_CreateCheckpoints(void*& context, int winHandler, short* options, short* projName, short* folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(CREATE_CHECKPOINTS, options, projName, folderName, output, outputMessage);
}

int SPF_ConvertStructures(void*& context, int winHandler, short* options, short* projName, short* folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(CONVERT_STRUCTURES_TO_SIMPLE, options, projName, folderName, output, outputMessage);
}

int SPF_InitDeclsWithZero(void*& context, int winHandler, short* options, short* projName, short* folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(SET_TO_ALL_DECL_INIT_ZERO, options, projName, folderName, output, outputMessage);
}

int SPF_RemoveUnusedFunctions(void*& context, int winHandler, short* options, short* projName, short* folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(REMOVE_UNUSED_FUNCTIONS, options, projName, folderName, output, outputMessage);
}

int SPF_ExpressionSubstitution(void*& context, int winHandler, short* options, short* projName, short* folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(SUBST_EXPR_RD_AND_UNPARSE, options, projName, folderName, output, outputMessage);
}

int SPF_InsertDvmhRegions(void*& context, int winHandler, short* options, short* projName, short* folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    ignoreArrayDistributeState = true;
    return simpleTransformPass(INSERT_REGIONS, options, projName, folderName, output, outputMessage);
}

int SPF_ResolveCommonBlockConflicts(void*& context, int winHandler, short* options, short* projName, short* folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    ignoreArrayDistributeState = true;
    return simpleTransformPass(FIX_COMMON_BLOCKS, options, projName, folderName, output, outputMessage);
}

int SPF_SharedMemoryParallelization(void*& context, int winHandler, short* options, short* projName, short* folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    ignoreArrayDistributeState = true;
    sharedMemoryParallelization = 1;
    return simpleTransformPass(INSERT_PARALLEL_DIRS_NODIST, options, projName, folderName, output, outputMessage, true);
}

int SPF_InsertPrivateFromGUI(void*& context, int winHandler, short* options, short* projName, short* folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(INSERT_NO_DISTR_FLAGS_FROM_GUI, options, projName, folderName, output, outputMessage);
}

int SPF_RemoveDeadCode(void*& context, int winHandler, short* options, short* projName, short* folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(REMOVE_DEAD_CODE_AND_UNPARSE, options, projName, folderName, output, outputMessage);
}

int SPF_InsertImplicitNone(void*& context, int winHandler, short* options, short* projName, short* folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(SET_IMPLICIT_NONE, options, projName, folderName, output, outputMessage);
}

int SPF_RenameIncludes(void*& context, int winHandler, short* options, short* projName, short* folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(RENAME_INLCUDES, options, projName, folderName, output, outputMessage);
}

int SPF_InsertPrivateArrayDirectives(void*& context, int winHandler, short* options, short* projName, short* folderName, string& output, string& outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    ignoreArrayDistributeState = true;
    sharedMemoryParallelization = 1;
    return simpleTransformPass(FIND_PRIVATE_ARRAYS, options, projName, folderName, output, outputMessage);
}

int SPF_MoveOperators(void*& context, int winHandler, short* options, short* projName, short* folderName, string& output, string& outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(MOVE_OPERATORS, options, projName, folderName, output, outputMessage);
}

int SPF_MergeRegions(void*& context, int winHandler, short* options, short* projName, short* folderName, string& output, string& outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    return simpleTransformPass(MERGE_REGIONS, options, projName, folderName, output, outputMessage);
}

static inline void convertBackSlash(char *str, int strL)
{
    for (int z = 0; z < strL; ++z)
        if (str[z] == '\\')
            str[z] = '/';
}

//TODO: need to extend 'outFileName' to vector
static int inline runModificationPass(passes passName, short* projName, short* folderName, vector<pair<string, string>> &newFiles, const string outFileName)
{
    PASSES_DONE[passName] = 0;
    runPassesForVisualizer(projName, { passName }, folderName);

    //fill data
    // newFiles: <имена файлов для мод, содержимое файла>
    string newFile;
    
    if (SgFile::switchToFile(outFileName.c_str()) == -1)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    newFile = unparseProjectToString(current_file, EXPAND_EXTRACT_PAR_REGION);
    newFiles.push_back(make_pair(outFileName, newFile));
    return newFiles.size();
}

extern tuple<string, int, int, int> inData;
int SPF_ChangeSpfIntervals(void*& context, int winHandler, short *options, short *projName, short *folderName, string &output, string &outputMessage, 
                           short *fileNameToMod, int *toModifyLines, vector<pair<string, string>> &newFiles)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    clearGlobalMessagesBuffer();
    setOptions(options);

    int retCode = 0;
    try
    {
        int strL;
        char *file_c = ConvertShortToChar(fileNameToMod, strL);
        convertBackSlash(file_c, strL);

        std::get<0>(inData) = file_c;
        std::get<1>(inData) = toModifyLines[0];
        std::get<2>(inData) = toModifyLines[1];
        std::get<3>(inData) = toModifyLines[2];
        retCode = runModificationPass(EXPAND_EXTRACT_PAR_REGION, projName, folderName, newFiles, file_c);
    }
    catch (int ex)
    {
        __spf_print(1, "catch code %d\n", ex);
        if (ex == -99)
            return -99;
        else
            retCode = -1;
    }
    catch (...)
    {
        retCode = -1;
    }

    if (showDebug)
        printf("SAPFOR: return from DLL\n");
    MessageManager::setWinHandler(-1);
    return retCode;
}

extern vector<tuple<string, string, int>> inDataProc;
int SPF_InlineProcedure(void*& context, int winHandler, short *options, short* projName, short* folderName,
                        short* name, short* file, int line, 
                        string &output, string &outputMessage, vector<pair<string, string>>& newFiles)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    clearGlobalMessagesBuffer();
    setOptions(options);

    int retCode = 0;
    try
    {
        int tmp;
        char* name_c = ConvertShortToChar(name, tmp);
        char* file_c = ConvertShortToChar(file, tmp);
        convertBackSlash(file_c, tmp);

        inDataProc.push_back(std::make_tuple(name_c, file_c, line));
        retCode = runModificationPass(INLINE_PROCEDURES, projName, folderName, newFiles, file_c);
    }
    catch (int ex)
    {
        __spf_print(1, "catch code %d\n", ex);
        if (ex == -99)
            return -99;
        else
            retCode = -1;
    }
    catch (...)
    {
        retCode = -1;
    }

    if (showDebug)
        printf("SAPFOR: return from DLL\n");
    MessageManager::setWinHandler(-1);
    return retCode;
}

extern pair<string, int> inOnlyForloopOnPlace;
int SPF_LoopUnionCurrent(void*& context, int winHandler, short* options, short* projName, short* folderName,
                         short* file, int line,
                         string &output, string &outputMessage, vector<pair<string, string>> &newFiles)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    clearGlobalMessagesBuffer();
    setOptions(options, false, new set<int>{ KEEP_DVM_DIRECTIVES });

    int retCode = 0;
    try
    {
        int tmp;
        char* file_c = ConvertShortToChar(file, tmp);
        if (!file_c)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
        convertBackSlash(file_c, tmp);

        inOnlyForloopOnPlace = make_pair(file_c, line);
        retCode = runModificationPass(LOOPS_COMBINER, projName, folderName, newFiles, file_c);
    }
    catch (int ex)
    {
        __spf_print(1, "catch code %d\n", ex);
        if (ex == -99)
            return -99;
        else
            retCode = -1;
    }
    catch (...)
    {
        retCode = -1;
    }

    if (showDebug)
        printf("SAPFOR: return from DLL\n");
    MessageManager::setWinHandler(-1);
    return retCode;
}


extern map<string, set<pair<string, int>>> inDataChains;
extern set<string> inDataChainsStart;
int SPF_InlineProcedures(void*& context, int winHandler, short* options, short* projName, short* folderName,
                         short* names, string &output, string &outputMessage, int type)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    clearGlobalMessagesBuffer();
    setOptions(options, false, new set<int>{ KEEP_DVM_DIRECTIVES });

    int retCode = 0;
    try
    {
        int tmp = 0;
        char* names_c = ConvertShortToChar(names, tmp);
        if (tmp != 0)
        {
            string allNames(names_c);
            vector<string> result;
            splitString(allNames, '|', result);

            if (type == 0) // dot substitution
            {
                if (result.size() < 2)
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                for (int z = 0; z < result.size(); )
                {
                    string procName = result[z++];
                    int count = -1;
                    if (sscanf(result[z++].c_str(), "%d", &count) == -1)
                        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                    if (result.size() < z + 2 * count)
                        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                    for (int k = z; k < z + 2 * count; k += 2)
                    {
                        int line = -1;
                        if (sscanf(result[k + 1].c_str(), "%d", &line) == -1)
                            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                        inDataProc.push_back(make_tuple(procName, result[k], line));
                    }
                    z += 2 * count;

                    if (count == 0)
                        inDataProc.push_back(make_tuple(procName, procName, -1));
                }

                PASSES_DONE[INLINE_PROCEDURES] = 0;
                runPassesForVisualizer(projName, { INLINE_PROCEDURES }, folderName);

                inDataProc.clear();
            }
            else if (type == 1) // hierarchical substitution
            {
                int numOfChain = -1;
                int z = 0;

                if (sscanf(result[z++].c_str(), "%d", &numOfChain) == -1)
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                for (int k = 0; k < numOfChain; ++k)
                {
                    int numOfCalls = -1;
                    if (sscanf(result[z++].c_str(), "%d", &numOfCalls) == -1)
                        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                    string last = "";
                    for (int p = 0; p < numOfCalls; ++p)
                    {
                        string funcName = result[z++];
                        string fileName = result[z++];
                        int line = -1;
                        if (sscanf(result[z++].c_str(), "%d", &line) == -1)
                            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                        int needInline = -1;
                        if (sscanf(result[z++].c_str(), "%d", &needInline) == -1)
                            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                        if (last != "" && needInline == 1)
                            inDataChains[last].insert(make_pair(funcName, line));

                        if (last == "" && needInline == 1)
                            inDataChainsStart.insert(funcName);

                        if (needInline == 1)
                            last = funcName;
                        else
                            last = "";
                    }
                }
                
                PASSES_DONE[INLINE_PROCEDURES] = 0;
                runPassesForVisualizer(projName, { INLINE_PROCEDURES }, folderName);

                inDataChains.clear();
            }
            else
                retCode = -1;
        }
    }
    catch (int ex)
    {
        __spf_print(1, "catch code %d\n", ex);
        if (ex == -99)
            return -99;
        else
            retCode = -1;
    }
    catch (...)
    {
        retCode = -1;
    }

    convertBuffers(outputMessage, output);

    if (showDebug)
        printf("SAPFOR: return from DLL\n");
    MessageManager::setWinHandler(-1);
    return retCode;
}


extern map<string, map<int, set<string>>> filesToInclude;
int SPF_InsertIncludesPass(void*& context, int winHandler, short *options, short *projName, short *folderName, char *visFilesToInclude,
                           string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);

    if (visFilesToInclude == NULL)
        return -2;

    vector<string> splited;
    //printf("%s\n", conv);
    splitString(visFilesToInclude, '|', splited);

    if (splited.size() == 0)
        return -3;

    filesToInclude.clear();
    unsigned i = 0;
    while (i < splited.size())
    {
        string file = splited[i++];
        int num = 0;
        if (sscanf(splited[i++].c_str(), "%d", &num) == -1)
            return -4;

        __spf_print(1, "file = %s:\n", file.c_str());
        for (int k = i; k < i + 2 * num; k += 2)
        {
            int line = 0;
            if (sscanf(splited[k].c_str(), "%d", &line) == -1)
                return -5;

            filesToInclude[file][line].insert(splited[k + 1]);
            __spf_print(1, "    include = [%d %s]\n", line, splited[k + 1].c_str());
        }
        i += 2 * num;
    }
    return simpleTransformPass(INSERT_INCLUDES, options, projName, folderName, output, outputMessage);
}

int SPF_LoopEndDoConverterPass(void*& context, int winHandler, short *options, short *projName, short *folderName, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);

    return simpleTransformPass(CONVERT_TO_ENDDO, options, projName, folderName, output, outputMessage);
}

extern map<string, map<int, Gcov_info>> gCovInfo;
int SPF_GetGCovInfo(void*& context, int winHandler, short *options, short *projName, string &result, string &output, string &outputMessage)
{
    MessageManager::clearCache();
    MessageManager::setWinHandler(winHandler);
    clearGlobalMessagesBuffer();
    setOptions(options);

    int retSize = -1;
    try
    {
        runPassesForVisualizer(projName, { GCOV_PARSER });

        json gcov_array = json::array();
        for (auto& byFile : gCovInfo)
        {
            json fileGCov;
            fileGCov["file"] = byFile.first;

            json info_array = json::array();
            for (auto& elem : byFile.second)
            {
                json item;
                item["line"] = elem.first;
                item["execution"] = elem.second.getExecutedCount();

                info_array.push_back(item);
            }
            fileGCov["lines"] = info_array;

            gcov_array.push_back(fileGCov);
        }

        json allGCov;
        allGCov["allGCov"] = gcov_array;
        result = allGCov.dump();        
        retSize = result.size();
    }
    catch (int ex)
    {
        try { __spf_print(1, "catch code %d\n", ex); }
        catch (...) {}

        if (ex == -99)
            return -99;
        else
            retSize = -1;
    }
    catch (...)
    {
        retSize = -1;
    }

    convertBuffers(outputMessage, output);

    if (showDebug)
        printf("SAPFOR: return from DLL\n");
    MessageManager::setWinHandler(-1);
    return retSize;
    
}

extern void deleteAllAllocatedData(bool enable);
extern set<short*> allocated;

void SPF_deleteAllAllocatedData(void*& context)
{
    MessageManager::clearCache();

    for (auto& elem : allocated)
        delete []elem;
    allocated.clear();


    deleteAllAllocatedData(true);
}

void createNeededException()
{
    if (passDone == 2)
        throw std::runtime_error(interruptEx);
}

static void* context = NULL;
static short* toShort(const char* str)
{
    int len = str != NULL ? strlen(str) : 0; 
    short* arr = new short[len + 1];
    for (int z = 0; z < len; ++z)
        arr[z] = str[z];
    arr[len] = '\0';
    return arr;
}

template <typename STR>
static STR toString(const short* array, int size)
{
    if (size == 0)
        return STR();

    STR str;
    for (int z = 0; z < size; ++z)
        str += array[z];
    return str;
}

static wstring toWstring(const int* array, int size)
{
    if (size == 0)
        return L"";

    wstring str = L"";
    for (int z = 0; z < size; ++z)
    {
        if (z != 0)
            str += L"|";
        str += std::to_wstring(array[z]);
    }
    return str;
}

static void codeInfo(wstring& total, const wstring& toAdd)
{
    total += std::to_wstring(toAdd.size()) + L" " + toAdd;
}

static wstring finishJniCall(int retCode, const string& result, const string& output, const string& outputMessage, 
                             const string *predictorStats = nullptr, const vector<pair<string, string>>* newFiles = nullptr)
{
    json results;
    results["errorCode"] = retCode;
    results["result"] = result;
    results["output"] = output;
    results["outputMessage"] = outputMessage;
    results["predictorStats"] = predictorStats ? *predictorStats : "";

    json filesToModify = json::array();
    if (newFiles)
    {
        for (auto& [name, text] : *newFiles)
        {
            json newFile;
            newFile["name"] = name;
            newFile["text"] = text;

            filesToModify.push_back(newFile);
        }
    }
    results["files"] = filesToModify;

    string resVal = results.dump();
    short* resValS;
    copyStringToShort(resValS, resVal);

    wstring codedResult = toString<wstring>(resValS, resVal.size());
    return codedResult;
}

static wstring finishJniCall(const int size, const int* sizes, const short* newFilesNames, const short* newFiles)
{
    wstring codedResult = L"";

    codeInfo(codedResult, toWstring(sizes, (sizes) ? (size + 1) : 0));
    codeInfo(codedResult, toString<wstring>(newFilesNames, (newFilesNames) ? strLen(newFilesNames) : 0));
    codeInfo(codedResult, toString<wstring>(newFiles, (newFiles) ? strLen(newFiles) : 0));

    return codedResult;
}

static void fillInfo(const vector<string>& data, int64_t*& arr1, int& arr2)
{
    if (data.size() < 4)
        return;
    int idx = 0;
    arr2 = std::stoi(data[idx++]) * 3;
    arr1 = new int64_t[arr2];
    for (int z = 0; z < arr2; ++z, ++idx)
        arr1[z] = std::stoll(data[idx]);

    if (data.size() != idx)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
}

static void fillInfo(const string& data, int*& arr)
{
    vector<string> splited;
    splitString(data, '|', splited);

    int idx = 0;
    int count = splited.size();
    arr = new int[count];
    for (int z = 0; z < count; ++z, ++idx)
        arr[z] = std::stoi(splited[idx]);
}

static void fillInfo(const string& data, int64_t*& arr)
{
    vector<string> splited;
    splitString(data, '|', splited);

    int idx = 0;
    int count = splited.size(); 
    arr = new int64_t[count];
    for (int z = 0; z < count; ++z, ++idx)
        arr[z] = std::stoll(splited[idx]);
}

const wstring Sapfor_RunAnalysis(const char* analysisName_c, const char* options_c, const char* projName_c, int winHandler)
{
    const string whichRun = analysisName_c;
    int retCode = 0;

    string result, output, outputMessage;

    short* projSh = toShort(projName_c);
    short* optSh = toShort(options_c);

    try
    {
        if (whichRun == "SPF_GetGraphLoops")
            retCode = SPF_GetGraphLoops(context, winHandler, optSh, projSh, result, output, outputMessage);
        else if (whichRun == "SPF_GetGraphFunctions")
            retCode = SPF_GetGraphFunctions(context, winHandler, optSh, projSh, result, output, outputMessage);
        else if (whichRun == "SPF_GetGraphFunctionPositions")
            retCode = SPF_GetGraphFunctionPositions(context, winHandler, optSh, projSh, result, output, outputMessage);
        else if (whichRun == "SPF_GetArrayDistribution")
            retCode = SPF_GetArrayDistribution(context, winHandler, optSh, projSh, result, output, outputMessage, 0);
        else if (whichRun == "SPF_GetArrayDistributionOnlyAnalysis")
            retCode = SPF_GetArrayDistribution(context, winHandler, optSh, projSh, result, output, outputMessage, 1);
        else if (whichRun == "SPF_GetArrayDistributionOnlyRegions")
            retCode = SPF_GetArrayDistribution(context, winHandler, optSh, projSh, result, output, outputMessage, 2);
        else if (whichRun == "SPF_SetFunctionsToInclude")
            retCode = SPF_SetFunctionsToInclude(context, winHandler, optSh, projSh, result, output, outputMessage);
        else if (whichRun == "SPF_GetAllDeclaratedArrays")
            retCode = SPF_GetAllDeclaratedArrays(context, winHandler, optSh, projSh, result, output, outputMessage);
        else if (whichRun == "SPF_GetFileLineInfo")
            retCode = SPF_GetFileLineInfo(context, winHandler, optSh, projSh, result, output, outputMessage);
        else if (whichRun == "SPF_GetIncludeDependencies")
            retCode = SPF_GetIncludeDependencies(context, winHandler, optSh, projSh, result, output, outputMessage);
        else if (whichRun == "SPF_GetGCovInfo")
            retCode = SPF_GetGCovInfo(context, winHandler, optSh, projSh, result, output, outputMessage);
        else if (whichRun == "SPF_ParseFiles")
            retCode = SPF_ParseFiles(context, winHandler, optSh, projSh, output, outputMessage);
        else if (whichRun == "SPF_ParseFilesWithOrder")
            retCode = SPF_ParseFilesWithOrder(context, winHandler, optSh, projSh, result, output, outputMessage);
        else if (whichRun == "SPF_StatisticAnalyzer")
            retCode = SPF_StatisticAnalyzer(context, winHandler, optSh, projSh, output, outputMessage);
        else if (whichRun == "SPF_GetPassesStateStr")
            retCode = SPF_GetPassesStateStr(context, result);
        else if (whichRun == "SPF_GetVersionAndBuildDate")
            retCode = SPF_GetVersionAndBuildDate(context, result);
        else if (whichRun == "SPF_GetCurrentPID")
            retCode = SPF_GetCurrentPID(context, result);
        else if (whichRun == "SPF_GetIntrinsics")
            retCode = SPF_GetIntrinsics(context, result);
        else if (whichRun == "SPF_deleteAllAllocatedData")
            SPF_deleteAllAllocatedData(context);
        else if (whichRun == "SPF_GetArrayLinks")
            retCode = SPF_GetArrayLinks(context, winHandler, optSh, projSh, result, output, outputMessage);
        else if (whichRun == "SPF_GetMaxMinBlockDistribution")
            retCode = SPF_GetMaxMinBlockDistribution(context, winHandler, optSh, projSh, result, output, outputMessage);
        else if (whichRun == "SPF_СhangeDirectory") // russian C
        {
            if (options_c == NULL)
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            if (chdir(options_c) != 0)
            {
                __spf_print(1, "can not change directory to '%s'\n", options_c);
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            }
        }
        else if (whichRun == "SPF_ChangeDirectory")
        {
            if (options_c == NULL)
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            if (chdir(options_c) != 0)
            {
                __spf_print(1, "can not change directory to '%s'\n", options_c);
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            }
        }        
        else if (whichRun == "SPF_OpenDvmStatistic")
            retCode = SPF_OpenDvmStatistic(context, projSh, result);
        else
        {
            if (showDebug)
                printf("SAPFOR: unknown function call, given '%s' name\n", whichRun.c_str());
            retCode = -1001;
        }
    }
    catch (...)
    {
        printf("SAPFOR: wrong exit from main DLL block for JAVA\n");
        convertBuffers(outputMessage, output);
        retCode = -1004;
    }

    delete[]projSh;
    delete[]optSh;

    fflush(NULL);
    const wstring codedResult = finishJniCall(retCode, result, output, outputMessage);
    return codedResult;
}

const wstring Sapfor_RunTransformation(const char* transformName_c, const char* options_c, const char* projName_c,
                                       const char* folder_c, const char* addOpt_c, int winHandler)
{
    const string whichRun = transformName_c;
    int retCode = 0;

    string result, output, outputMessage, predStats;

    short* projSh = toShort(projName_c);
    short* optSh = toShort(options_c);
    short* fold = toShort(folder_c); if (string("") == folder_c) fold = NULL;
    short* addOpt = toShort(addOpt_c);

    if (whichRun == "SPF_CorrectCodeStylePass")
        retCode = SPF_CorrectCodeStylePass(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_RemoveOmpDirectives")
        retCode = SPF_RemoveOmpDirectives(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_RemoveDvmDirectives")
        retCode = SPF_RemoveDvmDirectives(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_RemoveSpfDirectives")
        retCode = SPF_RemoveSpfDirectives(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_RemoveDvmDirectivesToComments")
        retCode = SPF_RemoveDvmDirectivesToComments(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_RemoveComments")
        retCode = SPF_RemoveComments(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_InsertIncludesPass")
        retCode = SPF_InsertIncludesPass(context, winHandler, optSh, projSh, fold, (char*)addOpt_c, output, outputMessage);
    else if (whichRun == "SPF_ResolveParallelRegionConflicts")
        retCode = SPF_ResolveParallelRegionConflicts(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_RemoveDistArraysFromIO")
        retCode = SPF_RemoveDistArraysFromIO(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_LoopEndDoConverterPass")
        retCode = SPF_LoopEndDoConverterPass(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_LoopFission")
        retCode = SPF_LoopFission(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_LoopUnion")
        retCode = SPF_LoopUnion(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_LoopUnrolling")
        retCode = SPF_LoopUnrolling(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_PrivateExpansion")
        retCode = SPF_PrivateExpansion(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_PrivateShrinking")
        retCode = SPF_PrivateShrinking(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_PrivateRemoving")
        retCode = SPF_PrivateRemoving(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_CreateIntervalsTree")
        retCode = SPF_CreateIntervalsTree(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_RemoveDvmIntervals")
        retCode = SPF_RemoveDvmIntervals(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_DuplicateFunctionChains")
        retCode = SPF_DuplicateFunctionChains(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_InlineProcedures")
        retCode = SPF_InlineProcedures(context, winHandler, optSh, projSh, fold, addOpt, output, outputMessage, 0);
    else if (whichRun == "SPF_InlineProceduresH")
        retCode = SPF_InlineProcedures(context, winHandler, optSh, projSh, fold, addOpt, output, outputMessage, 1);
    else if (whichRun == "SPF_CreateCheckpoints")
        retCode = SPF_CreateCheckpoints(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_ConvertStructures")
        retCode = SPF_ConvertStructures(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_InitDeclsWithZero")
        retCode = SPF_InitDeclsWithZero(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_RemoveUnusedFunctions")
        retCode = SPF_RemoveUnusedFunctions(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_ExpressionSubstitution")
        retCode = SPF_ExpressionSubstitution(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_InsertDvmhRegions")
        retCode = SPF_InsertDvmhRegions(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_ResolveCommonBlockConflicts")
        retCode = SPF_ResolveCommonBlockConflicts(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_SharedMemoryParallelization")
        retCode = SPF_SharedMemoryParallelization(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_InsertPrivateFromGUI")
        retCode = SPF_InsertPrivateFromGUI(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_RemoveDeadCode")
        retCode = SPF_RemoveDeadCode(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_InsertImplicitNone")
        retCode = SPF_InsertImplicitNone(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_RenameIncludes")
        retCode = SPF_RenameIncludes(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_InsertPrivateArrayDirectives")
        retCode = SPF_InsertPrivateArrayDirectives(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_MoveOperators")
        retCode = SPF_MoveOperators(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_MergeRegions")
        retCode = SPF_MergeRegions(context, winHandler, optSh, projSh, fold, output, outputMessage);
    else if (whichRun == "SPF_CreateParallelVariant")
    {
        vector<string> splited;
        string orig(addOpt_c);
        splitString(orig, '|', splited);

        int64_t* variants = NULL;
        int varLen = -1;

        fillInfo(splited, variants, varLen);
        retCode = SPF_CreateParallelVariant(context, winHandler, optSh, projSh, fold, variants, &varLen, output, outputMessage, predStats);

        if (retCode > 0)
            delete[]variants;
    }
    else
    {
        if (showDebug)
            printf("SAPFOR: unknown function call, given '%s' name\n", whichRun.c_str());
        retCode = -1002;
    }

    delete[]projSh;
    delete[]optSh;
    delete[]fold;
    delete[]addOpt;

    fflush(NULL);
    const wstring codedResult = finishJniCall(retCode, result, output, outputMessage, &predStats, nullptr);
    return codedResult;
}

const wstring Sapfor_RunModification(const char* modifyName_c, const char* options_c, const char* projName_c,
                                     const char* folder_c, const char* addOpt1_c, const char* addOpt2_c, 
                                     int winHandler)
{
    const string whichRun = modifyName_c;
    int retCode = 0;

    string result, output, outputMessage;    
    vector<pair<string, string>> newFiles;

    short* projSh = toShort(projName_c);
    short* optSh = toShort(options_c);
    short* fold = toShort(folder_c); 
    if (string("") == folder_c)
    {
        string tmp = "---modification---";
        
        delete[]fold;
        fold = new short[tmp.size() + 1];
        for (int z = 0; z < tmp.size(); ++z)
            fold[z] = tmp[z];
        fold[tmp.size()] = '\0';
    }

    if (whichRun == "SPF_ModifyArrayDistribution")
    {
        int regId = atoi(addOpt1_c);
        int64_t* modify = NULL;
        fillInfo(addOpt2_c, modify);

        retCode = SPF_ModifyArrayDistribution(context, winHandler, optSh, projSh, output, outputMessage, regId, modify);
        delete[]modify;
    }
    else if (whichRun == "SPF_InlineProcedure")
    {
        vector<string> splitS;
        splitString(addOpt1_c, '|', splitS);

        if (splitS.size() == 2)
        {
            vector<short*> tmpPar = { toShort(splitS[0].c_str()), toShort(splitS[1].c_str()) };
            int line = std::stoi(addOpt2_c);
            retCode = SPF_InlineProcedure(context, winHandler, optSh, projSh, fold, tmpPar[0], tmpPar[1], line, output, outputMessage, newFiles);

            delete[] tmpPar[0];
            delete[] tmpPar[1];
        }
        else
            retCode = -1;
    }
    else if (whichRun == "SPF_LoopUnionCurrent")
    {
        short* file = toShort(addOpt1_c);
        int line = std::stoi(addOpt2_c);
        retCode = SPF_LoopUnionCurrent(context, winHandler, optSh, projSh, fold, file, line, output, outputMessage, newFiles);
        delete[]file;
    }
    else if (whichRun == "SPF_ChangeSpfIntervals")
    {
        short* fileNameToMod = toShort(addOpt1_c);
        int* toModifyLines = NULL;
        fillInfo(addOpt2_c, toModifyLines);
        retCode = SPF_ChangeSpfIntervals(context, winHandler, optSh, projSh, fold, output, outputMessage, fileNameToMod, toModifyLines, newFiles);
        delete[]fileNameToMod;
        delete[]toModifyLines;
    }
    else if (whichRun == "SPF_SetDistributionFlagToArray")
        retCode = SPF_SetDistributionFlagToArray(context, (char*)addOpt1_c, atoi(addOpt2_c));
    else if (whichRun == "SPF_SetDistributionFlagToArrays")
        retCode = SPF_SetDistributionFlagToArrays(context, addOpt1_c, addOpt2_c);
    else
    {
        if (showDebug)
            printf("SAPFOR: unknown function call, given '%s' name\n", whichRun.c_str());
        retCode = -1003;
    }

    delete[]projSh;
    delete[]optSh;
    delete[]fold;

    fflush(NULL);
    convertBuffers(outputMessage, output);
    const wstring codedResult = finishJniCall(retCode, result, output, outputMessage, nullptr, &newFiles);
    
    //wprintf(L"%s\n", codedResult.c_str());
    return codedResult;
}
