#include "../Utils/leak_detector.h"

#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <string>
#include <algorithm>

#include "DvmhDirective.h"
#include "../Distribution/Array.h"
#include "../Distribution/Arrays.h"
#include "../Distribution/GraphCSR.h"
#include "../Utils/errors.h"
#include "../Utils/utils.h"
#include "../GraphCall/graph_calls_func.h"

using std::vector;
using std::string;
using std::pair;
using std::set;
using std::map;


static inline string calculateShiftsNoDist(
    DIST::Array* arrayRef, DIST::Array* calcForArray,
    pair<pair<string, string>, vector<pair<int, int>>>& coeffs,
    vector<pair<int, int>>& shifts,
    vector<map<pair<int, int>, int>>& shiftsByAccess,
    const map<DIST::Array*, pair<vector<ArrayOp>, vector<bool>>>& readOps,
    const map<DIST::Array*, set<DIST::Array*>>& arrayLinksByFuncCalls)
{
    string out = "";
    // check for distributed and not mapped dims -> zero them out ('coeffs.second')
    set<DIST::Array*> refs;
    getRealArrayRefs(calcForArray, calcForArray, refs, arrayLinksByFuncCalls);

    const pair<vector<ArrayOp>, vector<bool>>* currReadOp = NULL;
    auto readIt = readOps.find(calcForArray);
    if (readIt != readOps.end())
        currReadOp = &(readIt->second);

    const int len = (int)coeffs.second.size();

    bool allZero = true;
    for (int k = 0; k < len; ++k)
    {
        shiftsByAccess.push_back(map<pair<int, int>, int>());

        if (k != 0)
            out += ",";
        char buf[256];

        // calculate correct shifts from readOp info
        if (currReadOp)
        {
            // no unrecognized read operations
            if (currReadOp->second[k] == false)
            {
                for (auto& coefs : currReadOp->first[k].coefficients)
                {
                    auto currAccess = coefs.first;

                    const int currShift = coefs.first.second;

                    auto itFound = shiftsByAccess[k].find(currAccess);
                    if (itFound == shiftsByAccess[k].end())
                        itFound = shiftsByAccess[k].insert(itFound, make_pair(currAccess, currShift));
                }
            }
        }

        sprintf(buf, "%d:%d", coeffs.second[k].first, coeffs.second[k].second);
        shifts[k] = {0, 0};

        if (coeffs.second[k].first != 0 || coeffs.second[k].second != 0)
            allZero = false;
        out += buf;
    }

    if (allZero)
        return "";
    else
        return out;
}

string ParallelDirective::genBoundsNoDist(pair<pair<string, string>, vector<pair<int, int>>>& shadowOp,
    vector<pair<int, int>>& shadowOpShift,
    DIST::Array* currArray,
    const map<DIST::Array*, pair<vector<ArrayOp>, vector<bool>>>& readOps,
    set<DIST::Array*>& arraysInAcross,
    vector<map<pair<int, int>, int>>& shiftsByAccess,
    const map<DIST::Array*, set<DIST::Array*>>& arrayLinksByFuncCalls) const
{
    arraysInAcross.insert(currArray);
    return calculateShiftsNoDist(arrayRef, currArray, shadowOp, shadowOpShift, shiftsByAccess, readOps, arrayLinksByFuncCalls);
}
