#pragma once
#include "data_flow.h"

#include<vector>
#include<set>

#include "SgUtils.h"
#include "../CFGraph.h"
#include "../IR.h"

/* Note: this file should be included in data_flow.h to provide template definitions */

/* definitions for DataFlowAnalysisNode class */

template <class DataType>
const int DataFlowAnalysisNode<DataType>::CNT_NOTINIT;

template <class DataType>
DataFlowAnalysisNode<DataType>::DataFlowAnalysisNode()
{
    getRollback() = {};
    prev_blocks = {};
}

template <class DataType>
void DataFlowAnalysisNode<DataType>::doStep()
{
    int in_max_cnt = CNT_NOTINIT, out_max_cnt = CNT_NOTINIT;
    
    bool uniq_change = updateState();
    for (auto next : prev_blocks)
    {
        if (in_cnt < next->out_cnt)
        {
            if (next->out_cnt > in_max_cnt)
                in_max_cnt = next->out_cnt;

            const auto& byOut = next->getOut();
            bool inserted = addIn( byOut);

            if (inserted)
            {
                auto status = forwardData(byOut);
                inserted = status != DATA_FLOW_UPD_STATUS::NO_CHANGE;

                if (inserted && next->out_cnt > out_max_cnt)
                    out_max_cnt = next->out_cnt;

                uniq_change |= status == DATA_FLOW_UPD_STATUS::GENERATED;
            }
        }
    }

    uniq_change |= (out_cnt == CNT_NOTINIT);

    if (out_max_cnt != CNT_NOTINIT)
        out_cnt = out_max_cnt;

    if (in_max_cnt != CNT_NOTINIT)
        in_cnt = in_max_cnt;

    // TODO: fix counter overflow
    if (uniq_change)
    {
        out_cnt++;
        in_cnt++;
    }
}

/* definitions for DataFlowAnalysis class */

template <class NodeType>
void DataFlowAnalysis<NodeType>::analyze()
{
    auto curr = 0;
    auto stop = nodes.size();

    while (curr != stop)
    {
        auto curr_bb = nodes[curr];
        curr_bb->doStep();

        const auto& jumps = curr_bb->getRollback();
        if (jumps.size() != 0)
        {
            bool jump = false;
            for (const auto& jump_to : jumps)
            {
                if (curr_bb->newerThan(nodes[jump_to]))
                {
                    jump = true;
                    curr = jump_to;
                    break;
                }
            }

            if (jump)
                continue;
        }

        curr++;
    }
}
    
template <class NodeType>
DataFlowAnalysis<NodeType>::~DataFlowAnalysis()
{
    for (NodeType* node : nodes)
        delete node;

    nodes.clear();
}