# -*- encoding: utf-8 -*-
"""
 @Author: packy945
 @FileName: TreeNode.py
 @DateTime: 2023/5/15 14:27
 @SoftWare: PyCharm
"""

import re
import tkinter as tk
from data import *
import tkinter.messagebox


sz = []
A = Aexp([0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0])
img_open = []
img_png = []

class Tree:
    def __init__(self):
        self.bc = 40
        self.Node = []
        self.rootID = None
        self.deepth = []
        self.place = []
        self.mark = [0] * 110
        self.ck = []

    def check(self):
        self.ck = [1] * len(self.Node)
        for N in self.Node:
            if N[3]:
                self.ck[N[3]] = 0
            if N[4]:
                self.ck[N[4]] = 0

    def remark(self):
        self.mark = [0] * 110



    def deep(self):
        flag = 0
        for i in range(len(self.Node)):
            if self.Node[i][1] == 'Value':
                self.deepth[i] = 0

        for i in range(len(self.Node)):
            if self.deepth[i] == -1:
                flag = 1
            if self.Node[i][3] != None and self.Node[i][4] != None:
                # print(self.Node[i][3], self.Node[i][4])
                self.deepth[i] = max(self.deepth[self.Node[i][3]], self.deepth[self.Node[i][4]]) + 1
        # print(self.deepth)
        if flag:
            self.deep()

    def calculate(self, node):
        print(node)
        if node[1] == 'Value':
            return node[5] * 1.0
        elif node[1] == 'Operator':
            a = self.calculate(self.Node[node[3]])
            b = self.calculate(self.Node[node[4]])
            if node[2] == '^':
                return pow(int(a), int(b))
            else:
            # print(a,node[2],b)
                return eval(str(a) + node[2] + str(b))

    def Aexp(self, node):

        if node[1] == 'Value':
            return str(node[5])
        elif node[1] == 'Operator':
            a = self.Aexp(self.Node[node[3]])
            b = self.Aexp(self.Node[node[4]])
            # print(a,node[2],b)
            ans = '(' + str(a) + node[2] + str(b) + ')'
            # print(ans)
            return ans


tree = Tree()


cur = 0
class TreeNode:
    def __init__(self, Type, value, left=None, right=None, color='方块'):
        global cur
        self.NodeID = cur  # 节点编号
        cur += 1
        self.NodeType = Type  # 节点类型
        self.Ops = None  # 运算符类型
        self.left = left
        self.right = right
        self.LeftNodeID = None
        self.RightNodeID = None
        if self.left:
            self.LeftNodeID = left.NodeID  # 左节点编号
        if self.right:
            self.RightNodeID = right.NodeID  # 右节点编号
        self.FaceValue = None  # 节点数值
        self.FaceColor = color  # 节点花色
        if Type == 'Value':
            self.FaceValue = value
        else:
            self.Ops = value

        tree.Node.append([self.NodeID, self.NodeType, self.Ops, self.LeftNodeID, self.RightNodeID, self.FaceValue, self.FaceColor])



def build_ast(formula):
    '''
    建立语法树
    :param formula: 表达式，表达式的花色
    :return: 建立好的语法树
    '''
    # 去掉空格

    global tree, cur
    try:
        expr, color = formula
    except:
        expr = formula
        color = ['方块'] * 20
    if len(expr) == 0:
        tk.messagebox.showinfo('', '表达式不存在')
        return
    cur = 0
    tree.__init__()

    expr = expr.replace(' ', '')

    # 将所有数字和符号分离出来
    tokens = re.findall(r'\d+|[()+\-*/^.]', expr)
    i = 0
    for token in tokens:
        if tokens[i] == '.':

            tokens[i - 1] = tokens[i - 1] + tokens[i] + tokens[i + 1]
            del tokens[i: i + 2]
        i += 1

    # 定义优先级
    precedence = {'+': 1, '-': 1, '*': 2, '/': 2, '^': 3}

    # 创建一个操作符栈和一个节点栈
    op_stack = []
    node_stack = []

    color_cur = 0
    # 遍历所有 token
    for token in tokens:
        if token.isdigit():
            # print(cur, color[cur])
            node_stack.append(TreeNode('Value', int(token), color=color[color_cur]))
            color_cur += 1
        elif token.count('.') == 1:
            node_stack.append(TreeNode('Value', float(token), color=color[color_cur]))
            color_cur += 1
        elif token in '+-*/^':
            while op_stack and op_stack[-1] != '(' and precedence[token] <= precedence[op_stack[-1]]:
                op = op_stack.pop()
                right = node_stack.pop()
                left = node_stack.pop()
                node_stack.append(TreeNode('Operator', op, left, right))
            op_stack.append(token)
        elif token == '(':
            op_stack.append(token)
        elif token == ')':
            while op_stack and op_stack[-1] != '(':
                op = op_stack.pop()
                right = node_stack.pop()
                left = node_stack.pop()
                node_stack.append(TreeNode('Operator', op, left, right))
            op_stack.pop()

    # 处理剩下的操作符
    while op_stack:
        op = op_stack.pop()
        right = node_stack.pop()
        left = node_stack.pop()
        node_stack.append(TreeNode('Operator', op, left, right))

    # 返回抽象语法树的根节点
    tree.rootID = node_stack[0].NodeID
    # print(tree.rootID)
    # print(tree.Node)
    return node_stack[0]


def print_ast(node:TreeNode, prefix='', is_left=True):
    if node:
        if node.NodeType == 'Value':
            s = str(node.FaceValue)
        else:
            s = str(node.Ops)
        print(prefix + ('├── ' if is_left else '└── ') + s)
        print_ast(node.left, prefix + ('│   ' if is_left else '    '), True)
        print_ast(node.right, prefix + ('│   ' if is_left else '    '), False)






if __name__ == '__main__':
    expr = "(34-13.121)*2*6"
    # ast = build_ast(expr)
    # print_ast(ast)
    # print(tree.Node)
    class Frame_Tree:
        # def Frame_tree():
        def __init__(self, window):
            self.Tree_root = tk.Frame(window, relief="groove", width=610, height=400)
            self.Tree_root.place(x=50, y=250)  # 树的显示

            self.Tree_cv = tk.Canvas(self.Tree_root, width=610, height=400, bg='#b7bcc0')
            self.Tree_cv.place(x=0, y=0)
            # Tree_cv.create_rectangle(10, 10, 600, 290, width=10, outline='#ff9933')

            bot = tk.Button(self.Tree_root, text='添加运算节点', bg='#cca583', fg='black', )
            bot.place(x=500, y=40, width=80, height=30)
            bot = tk.Button(self.Tree_root, text='添加数值节点', bg='#cca583', fg='black', )
            bot.place(x=500, y=75, width=80, height=30)
            bot = tk.Button(self.Tree_root, text='改变运算输入', bg='#cca583', fg='black', )
            bot.place(x=500, y=110, width=80, height=30)
            bot = tk.Button(self.Tree_root, text='改变节点运算', bg='#cca583', fg='black', )
            bot.place(x=500, y=145, width=80, height=30)
            bot = tk.Button(self.Tree_root, text='数值节点交换', bg='#cca583', fg='black', )
            bot.place(x=500, y=180, width=80, height=30)
            bot = tk.Button(self.Tree_root, text='产生表达式', bg='#cca583', fg='black', command=lambda: self.Aexp())
            bot.place(x=500, y=215, width=80, height=30)
            # bot = tk.Button(self.Tree_root, text='计算', bg='#cca583', fg='black', command=lambda: tree.calculate(tree.Node[tree.rootID]))
            # bot.place(x=500, y=250, width=80, height=30)




        def clear(self):
            self.Tree_cv.delete('all')
            tree.Node = []
            tree.rootID = None

        def Aexp(self):
            entry.delete(0, "end")
            print(tree.Aexp(tree.Node[tree.rootID]))
            entry.insert("end", tree.Aexp(tree.Node[tree.rootID]))
            label2 = tk.Label(window, text=f'= {float(tree.calculate(tree.Node[tree.rootID])):.2f}',
                              font=('宋体', 15, 'bold'), width=30, height=10,
                              # 设置填充区距离、边框宽度和其样式（凹陷式）
                              borderwidth=5, relief="sunken")
            label2.place(x=240, y=160, width=60, height=50)

        def show_tree(self, ss):
            # 清空画布
            self.Tree_cv.delete('all')
            build_ast(ss)
            for i in range(len(tree.Node)):
                tree.deepth.append(-1)
            # print(tree.Node)
            # print(tree.deepth)
            # 格式化树节点的深度
            tree.deep()
            # print(tree.deepth)

            # 初始化节点数与深度
            dept, nums = -1, 0
            for i in range(len(tree.deepth)):
                dept = max(dept, tree.deepth[i])
                tree.place.append((None, None))
                if tree.deepth[i] == 0:
                    nums += 1
            # print(dept, nums)

            tree_length = 500
            tree_height = 400
            dx = tree_length // (nums + 1)
            dy = tree_height // (dept + 2)
            bc = 40
            # 若为数字节点
            cur = 0
            for i in range(len(tree.deepth)):
                if tree.deepth[i] == 0:
                    cur += 1
                    tree.place[i] = (cur * dx, tree_height - dy)
                    self.Tree_cv.create_rectangle(tree.place[i][0] - bc / 2, tree.place[i][1] - bc / 2,
                                                  tree.place[i][0] + bc / 2,
                                                  tree.place[i][1] + bc / 2, fill='white', width=0)
                    self.Tree_cv.create_text(tree.place[i][0], tree.place[i][1], text=str(tree.Node[i][5]),
                                             font=('宋体', 15, 'bold'), anchor='center')

            for d in range(dept):
                cur_d = d + 1
                for i in range(len(tree.deepth)):
                    if tree.deepth[i] == cur_d:
                        lnode = tree.place[tree.Node[i][3]]
                        rnode = tree.place[tree.Node[i][4]]
                        node = ((lnode[0] + rnode[0]) / 2, tree_height - (cur_d + 1) * dy)
                        self.Tree_cv.create_line(lnode[0], lnode[1] - bc / 2, node[0], node[1] + bc / 2, fill='#1ffbe9',
                                                 width=2)
                        self.Tree_cv.create_line(rnode[0], rnode[1] - bc / 2, node[0], node[1] + bc / 2, fill='#1ffbe9',
                                                 width=2)
                        tree.place[i] = node
                        self.Tree_cv.create_rectangle(tree.place[i][0] - bc / 2, tree.place[i][1] - bc / 2,
                                                      tree.place[i][0] + bc / 2, tree.place[i][1] + bc / 2,
                                                      fill='white', width=0)
                        self.Tree_cv.create_text(tree.place[i][0], tree.place[i][1], text=str(tree.Node[i][2]),
                                                 font=('宋体', 15, 'bold'), anchor='center')
    window = tk.Tk()
    var = tk.StringVar()
    var2 = tk.StringVar()
    width = 700
    height = 700  # 窗口大小
    screen_width = window.winfo_screenwidth()  # winfo方法来获取当前电脑屏幕大小
    screen_height = window.winfo_screenheight()
    x = int((screen_width - width) / 2)
    y = int((screen_height - height) / 2) - 40
    size = '{}x{}+{}+{}'.format(width, height, x, y)
    window.geometry(size)


    # 基本组件显示
    cv = tk.Canvas(window, width=380, height=136, bg='#FFFFCC')
    cv.place(x=100, y=3)
    F = Frame_Tree(window)
    but = tk.Button(window, text='出题', bg='#ffffcc', fg='black', command=lambda: show_card())
    but.place(x=500, y=20, width=100, height=50, )
    but2 = tk.Button(window, text='换牌', bg='#ffffcc', fg='black', )
    but2.place(x=500, y=80, width=100, height=50, )

    entry = tk.Entry(window, show=None, font=('宋体', 15, 'bold'))
    entry.place(x=50, y=160, width=180, height=50)
    label2 = tk.Label(window, text='= 24', font=('宋体', 15, 'bold'), width=30, height=10,
                      # 设置填充区距离、边框宽度和其样式（凹陷式）
                      borderwidth=5, relief="sunken")
    but4 = tk.Button(window, text='计算', bg='#ffffcc', fg='black', command=lambda: show_result(entry.get(), sz))
    but4.place(x=315, y=165, width=50, height=40, )
    label2.place(x=240, y=160, width=60, height=50)

    but3 = tk.Button(window, text='自动求解', bg='#ffffcc', fg='black', command=lambda: show_answer())
    but3.place(x=250, y=210, width=120, height=40, )
    but3 = tk.Button(window, text='所有答案', bg='#ffffcc', fg='black', command=lambda: show_all())
    but3.place(x=410, y=210, width=120, height=40, )

    but5 = tk.Button(window, text='画树', bg='#ffffcc', fg='black', command=lambda: F.show_tree(expr))
    but5.place(x=80, y=210, width=120, height=40, )



    # cv1 = tk.Canvas(window, width=380, height=90, bg='#FFFFCC')
    # cv1.place(x=100, y=200)

    window.mainloop()
