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

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

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

#include "graph_calls_func.h"
#include "graphLayout/nodesoup.hpp"
#include "BuildGraph.h"

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

using namespace nodesoup;

static int getNextRandNum(int &N, const int SIZE, int curr)
{
    int ret = N % SIZE;
    ++N;
    while (ret == curr)
    {
        if (SIZE == 1)
            break;
        ret = N % SIZE;
        ++N;
    }

    return ret;
}

map<string, pair<int, int>>
buildLocationOfGraph(const map<string, vector<FuncInfo*>>& allFuncInfo, const int iters, const int coef, 
                     const unsigned int width, const unsigned int height, const set<string>* visible)
{
    if (visible && visible->size() == 0)
        return map<string, pair<int, int>>();

    map<string, FuncInfo*> mapOfFuncs;
    createMapOfFunc(allFuncInfo, mapOfFuncs);

    map<string, int> num;
    
    int z = 0;
    for (auto& elem : mapOfFuncs)
    {
        if (visible && visible->find(elem.first) == visible->end())
            continue;
        num[elem.first] = z++;
    }

    for (auto& elem : mapOfFuncs)
    {
        set<string> callsFrom;
        for (auto& callFromInfo : elem.second->callsFromDetailed)
        {
            auto& callFrom_ = callFromInfo.detailCallsFrom;
            callsFrom.insert(callFrom_.first);
        }
        callsFrom.insert(elem.second->externalCalls.begin(), elem.second->externalCalls.end());

        for (auto& call : callsFrom)
        {
            if (visible && visible->find(call) == visible->end())
                continue;
            if (num.find(call) == num.end())
                num[call] = z++;
        }
    }

    adj_list_t g;
    g.resize(z);

    vector<Point2D> positions;
    vector<double> radiuses = size_radiuses(g);

    set<pair<string, string>> edges;
    for (auto& elem : mapOfFuncs)
    {
        if (visible && visible->find(elem.first) == visible->end())
            continue;

        set<string> callsFrom;
        for (auto& callFromInfo : elem.second->callsFromDetailed)
        {
            auto& callFrom_ = callFromInfo.detailCallsFrom;
            callsFrom.insert(callFrom_.first);
        }
        callsFrom.insert(elem.second->externalCalls.begin(), elem.second->externalCalls.end());

        for (auto& call : callsFrom)
        {
            if (visible && visible->find(call) == visible->end())
                continue;

            const pair<string, string> key = make_pair(elem.first, call);
            if (edges.find(key) == edges.end())
            {
                edges.insert(key);
                g[num[elem.first]].push_back(num[call]);
                g[num[call]].push_back(num[elem.first]);
            }
        }
    }

    if (z == 1)
    {
        map<string, pair<int, int>> result;
        if (num.size() != 1)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        for (auto& elem : num)
            result[elem.first] = std::make_pair(width / 2, height / 2);
        return result;
    }

    positions = fruchterman_reingold(g, width, height, iters, coef);

    map<string, pair<int, int>> result;
    for (auto& elem : num)
        result[elem.first] = positions[num[elem.first]].toPair();
    return result;
}