#pragma once

#include <vector>
#include <map>
#include <set>
#include <string>

#include "graph_loops.h"
#include "CFGraph/CFGraph.h"

struct RegionInstruction
{
    arrayAccessingIndexes def, use, in, out;
};

class Region : public SAPFOR::BasicBlock {
public:
    Region() { header = nullptr; }

    Region(SAPFOR::BasicBlock block) : SAPFOR::BasicBlock::BasicBlock(block) { header = nullptr; }

    Region(LoopGraph* loop, const std::vector<SAPFOR::BasicBlock*>& Blocks);

    Region* getHeader() { return header; }

    void setHeader(Region* region) { header = region; }

    std::vector<Region*>& getBasickBlocks() { return basickBlocks; }

    void addBasickBlocks(Region* region) { basickBlocks.push_back(region); }

    const std::set<Region*>& getPrevRegions() { return prevRegions; }

    std::set<Region*>& getNextRegions() { return nextRegions; }

    void removeNextRegion(Region* region)
    {
        if (nextRegions.find(region) != nextRegions.end())
            nextRegions.erase(region);
    }

    void removePrevRegion(Region* region)
    {
        if (prevRegions.find(region) != prevRegions.end())
            prevRegions.erase(region);
    }

    void addPrevRegion(Region* region) { prevRegions.insert(region); }

    void addNextRegion(Region* region) { nextRegions.insert(region); }

    void replaceInPrevRegions(Region* source, Region* destination)
    {
        prevRegions.erase(destination);
        prevRegions.insert(source);
    }

    void replaceInNextRegions(Region* source, Region* destination)
    {
        nextRegions.erase(destination);
        nextRegions.insert(source);
    }

    std::set<Region*> getSubRegions() { return subRegions; }

    void addSubRegions(Region* region) { subRegions.insert(region); }

    std::vector<RegionInstruction> instructions;

    arrayAccessingIndexes array_def, array_use, array_out, array_in, array_priv;

private:
    std::vector<Region*> basickBlocks;
    std::set<Region*> subRegions;
    /*next Region which is BB for current BB Region*/
    std::set<Region*> nextRegions;
    /*prev Regions which is BBs for current BB Region*/
    std::set<Region*> prevRegions;
    Region* header;
};

bool hasCycle(Region* block, const std::set<Region*>& cycleBlocks, std::set<Region*>& visitedBlocks);

bool topologySort(std::vector<Region*>& basikBlocks, Region* header);