#pragma once
#include<vector>
#include<set>

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

enum class DATA_FLOW_UPD_STATUS { NO_CHANGE = 0, PROPAGATED, GENERATED };

template <class DataType>
class DataFlowAnalysisNode {
    static const int CNT_NOTINIT = 0;

    int in_cnt = CNT_NOTINIT, out_cnt = CNT_NOTINIT;

    std::set<int> rollback;

    std::set<DataFlowAnalysisNode<DataType>*> prev_blocks;

    SAPFOR::BasicBlock* bb;
public:
    DataFlowAnalysisNode();

    void doStep();

    virtual DataType getIn() = 0;
    virtual DataType getOut() = 0;

    virtual bool addIn(const DataType& data) = 0;
    virtual bool addOut(const DataType& data) = 0;

    virtual bool updateState() { return false; }
    virtual DATA_FLOW_UPD_STATUS forwardData(const DataType& data) = 0;

    bool newerThan(const DataFlowAnalysisNode<DataType>* block) const { return out_cnt > block->in_cnt; }

    int getInCnt() { return in_cnt; }
    int getOutCnt() { return out_cnt; }

    void setInCnt(int cnt) { in_cnt = cnt; }
    void setOutCnt(int cnt) { out_cnt = cnt; }
    static int getStartCounter() { return CNT_NOTINIT; }

    std::set<int>& getRollback() { return rollback; }

    std::set<DataFlowAnalysisNode<DataType>*>& getPrevBlocks() { return prev_blocks; }

    SAPFOR::BasicBlock* getBlock() { return bb; }
    void setBlock(SAPFOR::BasicBlock* b) { bb = b; }
};

template <class NodeType>
class DataFlowAnalysis {
protected:
    std::vector<NodeType*> nodes;

    virtual NodeType* createNode(SAPFOR::BasicBlock* block) = 0;
public:
    virtual void fit(const std::vector<SAPFOR::BasicBlock*>& blocks) = 0;
    void analyze();

    const std::vector<NodeType*>& getNodes() { return nodes; }

    ~DataFlowAnalysis();
};

#include "data_flow_impl.h"