import ast
import logging

from models.funcnode import get_script
from models.sentencenode import SuspectedSentenceNode
from utils.funclink import ProjectAnalyzer
from utils.ERRORLIST import error_list


def add_sentence_purpose(sentence_node_list, file_name, line_no, private_info_list_to_be_added):
    """

        Args:
            sentence_node_list:
            file_name: 所被添加purpose 的sentence_node 的 文件名
            line_no: 所被添加purpose 的sentence_node 的 所在行
            private_info_list_to_be_added: 用于添加 purpose 的（datatype,purpose)元素对
        Returns:

        """
    for sentence_node in sentence_node_list:
        if sentence_node.file_path == file_name and sentence_node.line_no == line_no:
            private_info_without_usage = [info for info in sentence_node.private_info if info[1] != "None"]
            for pair in private_info_list_to_be_added:
                # private_info 添加
                private_info_each = [(private[0], pair[1]) for private in sentence_node.private_info if
                                     private[1] == "None" and pair[1] != "None"]
                private_info_without_usage.extend(private_info_each)
                # purpose 添加
                if pair[1] != "None" and pair[1] not in sentence_node.purpose:
                    sentence_node.purpose.append(pair[1])
            # sentence_node.purpose = [purpose for purpose in
            #                          sentence_node.purpose + [item[1] for item in private_info_list_to_be_added] if
            #                          purpose != "Usage"]
            if "None" in sentence_node.purpose:
                sentence_node.purpose.remove("None")
            # if len(sentence_node.purpose) != 0:
            #     sentence_node.purpose.remove("Usage")
            if private_info_without_usage:
                sentence_node.private_info = private_info_without_usage
            break


def get_func_list(node, func_list=None):
    """

    Args:
        node: ast_node
        func_list: 代码调用的方法列表

    Returns:

    """
    if func_list is None:
        func_list = []
    if isinstance(node, ast.Call):
        if isinstance(node.func, ast.Attribute):
            func_list.append(node.func.attr)
        elif isinstance(node.func, ast.Name):
            func_list.append(node.func.id)
        if len(node.args) > 0:
            for arg in node.args:
                func_list = get_func_list(arg, func_list)
    elif isinstance(node, ast.List) or isinstance(node, ast.Tuple) or isinstance(node, ast.Set):
        for arg in node.elts:
            func_list = get_func_list(arg, func_list)
    elif isinstance(node, ast.Compare):
        for comp in node.comparators:
            func_list = get_func_list(comp, func_list)
        func_list = get_func_list(node.left, func_list)
    elif isinstance(node, ast.withitem):
        func_list = get_func_list(node.context_expr)
    return func_list


def parse_tree2nd(source_dir, p, node, lines, func_node_dict, node_list_1st, file_name, node_list=None,
                  func_name="__main__",
                  class_name=None):
    """

    Args:
        source_dir: -
        p: 函数调用关系的类
        node: -
        lines: -
        func_node_dict: 全项目函数调用图
        node_list_1st: 第一次便利结果
        file_name: -
        node_list: -
        func_name: -
        class_name: -

    Returns:

    """
    if node_list is None:
        node_list = []
    func_list = []  # node 中所有的方法
    if isinstance(node, ast.Expr) or isinstance(node, ast.Assign) or isinstance(node, ast.Return):
        func_list = get_func_list(node.value)
    elif isinstance(node, ast.For):
        func_list = get_func_list(node.iter)
    elif isinstance(node, ast.While) or isinstance(node, ast.If):
        func_list = get_func_list(node.test)
    elif isinstance(node, ast.With):
        for item in node.items:
            func_list = get_func_list(item, func_list)

    if len(func_list) > 0:
        func_call = []  # 该Func define node 调用的方法
        func_path = None
        if func_name is not None:
            func_path = file_name.replace("\\", '/').replace(
                source_dir.replace("\\", "/") + "/", '').replace('py',
                                                                 func_name).replace(
                '/', '.')
            if class_name is not None:
                func_path = file_name.replace("\\", '/').replace(
                    source_dir.replace("\\", '/') + "/", '').replace('py',
                                                                     class_name + '.' + func_name).replace(
                    '/', '.')
            # print(func_path)
            # func_path = source_dir.split("\\")[-1] + "." + func_path
            # print(func_path)
            try:
                func_call = p.find_direct_callee_func(func_path)
            except:
                pass

        func_list = list(set(func_list))
        # 隐私类型传递   func{data，usage} 中data 为空，则将func.usage赋予data,usage else extend
        # c(): a.b   b中有隐私，赋给 c
        private_info = []
        for func in func_list:
            for func_c in func_call:
                if func == func_c.split('.')[-1]:
                    if func_c in func_node_dict.keys():
                        for pair in func_node_dict[func_c]:  # 被调用的方法有什么隐私数据和操作 调用的该行就有什么数据和操作
                            private_info.append(pair)
                        add_sentence_purpose(node_list_1st, file_name, node.lineno, func_node_dict[func_c])
        script = get_script(node, lines)

        if len(private_info) > 0:
            sentence_node = SuspectedSentenceNode(file_name, node.lineno, private_word_list=None, purpose=None,
                                                  func_name=func_name,
                                                  private_info=private_info, script=script)
            # print(private_info)
            has = False
            for node_1st in node_list_1st:
                if sentence_node == node_1st:
                    has = True
                    break
            if not has:
                node_list.append(sentence_node)

    if isinstance(node, ast.ClassDef):
        for node_son in node.body:
            node_list = parse_tree2nd(source_dir, p, node_son, lines, func_node_dict, node_list_1st,
                                      file_name, node_list, func_name=node.name,
                                      class_name=node.name)
    elif isinstance(node, ast.FunctionDef):
        for node_son in node.body:
            node_list = parse_tree2nd(source_dir, p, node_son, lines, func_node_dict, node_list_1st,
                                      file_name, node_list, func_name=node.name,
                                      class_name=class_name)
    else:
        if hasattr(node, "body"):
            for node_son in node.body:
                node_list = parse_tree2nd(source_dir, p, node_son, lines, func_node_dict, node_list_1st,
                                          file_name, node_list, func_name=func_name,
                                          class_name=class_name)
        #
        if isinstance(node, ast.If):
            for node_son in node.orelse:
                node_list = parse_tree2nd(source_dir, p, node_son, lines, func_node_dict, node_list_1st,
                                          file_name, node_list, func_name=func_name,
                                          class_name=class_name)

    return node_list


def parse_files_2nd(file_list, source, func_node_dict, node_list1st):
    """

    Args:
        file_list: -
        source: -
        func_node_dict:函数调用图
        node_list1st: 第一次遍历结果

    Returns:
        -
    """
    p = ProjectAnalyzer(source, file_list)
    node_list = []

    for file_name in file_list:
        with open(file_name, encoding='utf-8') as file_single:
            lines = file_single.readlines()
            try:
                tree_root = ast.parse(''.join(lines))
            except SyntaxError as e:
                e.filename = file_name
                error_list.append(e)
                pass
            node_list_single = parse_tree2nd(source, p, tree_root, lines, func_node_dict,
                                             node_list1st, file_name, [])
            node_list.extend(node_list_single)

    return node_list
