import queue
import networkx as nx
from utils.utils import CONSTANTS


class Slicing:

    def __init__(self, max_hop=CONSTANTS.max_hop, max_statement=CONSTANTS.max_statement):
        self.max_hop = max_hop
        self.max_statement = max_statement

    def forward_dependency_slicing(self, node, graph: nx.MultiDiGraph, contain_node=False):

        n_nodes = len(graph.nodes)

        line_ctx = dict()
        visited = set()

        q = queue.Queue()
        q.put((node, 0))

        def cdg_view(v, u, t):
            return t == 'CDG'

        def cfg_view(v, u, t):
            return t == 'CFG'

        def ddg_view(v, u, t):
            return t == 'DDG'

        cdg = nx.subgraph_view(graph, filter_edge=cdg_view)
        cfg = nx.subgraph_view(graph, filter_edge=cfg_view)
        ddg = nx.subgraph_view(graph, filter_edge=ddg_view)

        n_statement = set() #用来存储已经处理的节点
        while len(n_statement) < self.max_statement:
            if len(n_statement) == n_nodes or q.empty():
                break
            #从队列中取出当前节点curr_v和跳转步数hop
            curr_v, hop = q.get()
            #获取当前节点的起始行号和结束行号，并将它们添加到line_ctx字典中，以表示该节点的源代码行。
            start_line = graph.nodes[curr_v]['startRow']
            end_line = graph.nodes[curr_v]['endRow']
            for i in range(start_line, end_line + 1):
                line_ctx[i] = graph.nodes[curr_v]['sourceLines'][i - start_line]
            
            n_statement.add(curr_v)

            if len(n_statement) >= self.max_statement:
                break
            
            p = curr_v
            # 如果当前节点p在CDG中，则从p开始遍历其前驱节点，直到找到一个不在CDG中的节点为止。
            if p in cdg.nodes:
                while len(list(cdg.predecessors(p))) != 0:
                    p = list(cdg.predecessors(p))[0]
                    start_line = graph.nodes[p]['startRow']
                    end_line = graph.nodes[p]['endRow']
                    for i in range(start_line, end_line + 1):
                        line_ctx[i] = graph.nodes[p]['sourceLines'][i - start_line]
                    n_statement.add(p)
                    # 如果已经处理了足够的节点，则跳出循环。
                    if len(n_statement) >= self.max_statement:
                        break

            #遍历DDG的前驱节点，并更新当前节点p和hop。
            for u in ddg.predecessors(curr_v):
                p = u
                start_line = graph.nodes[p]['startRow']
                end_line = graph.nodes[p]['endRow']
                for i in range(start_line, end_line + 1):
                    line_ctx[i] = graph.nodes[p]['sourceLines'][i - start_line]
                n_statement.add(p)
                if len(n_statement) >= self.max_statement:
                    break
                # 如果p在CDG中，则从p开始遍历其前驱节点，直到找到一个不在CDG中的节点为止。
                if p in cdg.nodes:
                    if len(list(cdg.predecessors(p))) != 0:
                        p = list(cdg.predecessors(p))[0]
                        start_line = graph.nodes[p]['startRow']
                        end_line = graph.nodes[p]['endRow']
                        for i in range(start_line, end_line + 1):
                            line_ctx[i] = graph.nodes[p]['sourceLines'][i - start_line]
                        n_statement.add(p)

            # 如果当前节点的hop值大于最大hop值，则跳过该节点。
            if hop+1 > self.max_hop:
                continue
            else:
                # 遍历CFG的前驱节点，并将它们加入队列中。
                for u in cfg.predecessors(curr_v):
                    if u not in visited and 'definition' not in graph.nodes[u]['nodeType']:
                        q.put((u, hop+1))
            visited.add(curr_v)

        # 如果当前节点不是包含节点，则从结果中删除该节点。
        if not contain_node:
            n_statement.remove(node)
            start_line = graph.nodes[node]['startRow']
            end_line = graph.nodes[node]['endRow']
            for i in range(start_line, end_line + 1):
                line_ctx.pop(i)

        # 获取结果中节点的起始行号和结束行号，并将它们按照升序排列，以生成最终的源代码行列表。
        line_list = list(line_ctx.keys())
        line_list.sort()
        ctx = []
        for i in range(0, len(line_list)):
            ctx.append(line_ctx[line_list[i]])

        # 生成包含已处理节点的子图
        subgraph = nx.subgraph(graph, n_statement)

        return "".join(ctx), line_list, subgraph

