import random

from PySide6.QtGui import QFont
from PySide6.QtWidgets import QApplication, QMainWindow, QGraphicsLineItem, QMessageBox, QGraphicsScene

import data
from graph_24 import *
from main_ui import Ui_MainWindow


class MainWindow(QMainWindow, Ui_MainWindow):
	def __init__(self):
		super().__init__()
		self.drawn_tree_id = set()  # 已经绘制了树的节点
		self.seq = 1  # 在有树的情况下，添加节点的序号，以确定新加的节点位置
		self.trees_root = []  # 保存所有树的根节点
		self.selected_id = []  # 选中的节点ID
		self.adds_id = set()  # 已经用过的ID
		self.value_nodes = []  # 数值节点
		# self.operator_nodes = []
		self.selected_ops = None  # 选择的运算符
		self.selected_value = None  # 当前选择的卡片值
		self.r_count = 0  # 记录所有答案点击次数
		self.offset = 0  # 语法树遍历时随深度平移长度，避免节点重合
		self.TreeNode = []
		self.Tree_4_nums = None
		self.setupUi(self)
		self.init_ui()
		self.init_slot()
		self.pushButton_q.clicked.connect(self.question_card)    # 出题
		self.pushButton_ex.clicked.connect(self.change_card)    # 换牌
		self.pushButton_autosv.clicked.connect(self.AutoTest_9_10)  # 自动求解
		self.pushButton_dtree.clicked.connect(self.draw_tree)    # 绘制语法树
		self.pushButton_ast.clicked.connect(self.calculate_button)  # 生成语法树
		self.pushButton_allan.clicked.connect(self.AutoTest)    # 自动求解
		self.clear_button.clicked.connect(self.secen_clear)  # 清空画布
		self.pushButton_add.clicked.connect(lambda: self.select_ops('+'))
		self.pushButton_sub.clicked.connect(lambda: self.select_ops('-'))
		self.pushButton_mul.clicked.connect(lambda: self.select_ops('*'))
		self.pushButton_div.clicked.connect(lambda: self.select_ops('/'))
		self.add_val_node.clicked.connect(self.add_value_node)  # 添加数值节点
		self.add_ops_node.clicked.connect(self.add_operator_node)    # 添加运算符节点
		self.ex_node_value.clicked.connect(self.exchange_value_node)    # 修改数值节点
		self.ex_node_ops.clicked.connect(self.exchange_ops_node)    # 修改运算符节点
		self.ex_node_self.clicked.connect(self.exchange_two_nodes)  # 交换两个节点
		self.pushButton_exp.clicked.connect(self.agent_exp) # 生成表达式
		self.pushButton_check_te.clicked.connect(self.check_tree_and_expression)     # 验证语法树和表达式是否匹配

	def secen_clear(self):
		self.scene.clear()
		self.selected_id = []    # 选中的节点ID
		self.value_nodes = []    # 数值节点
		self.TreeNode = []      # 所有节点
		self.adds_id = set()    # 已经用过的ID
		self.trees_root = []    # 保存所有树的根节点
		self.drawn_tree_id = set()  # 已经绘制了树的节点
		self.selected_value = None  # 当前选择的卡片值.
		self.selected_ops = None  # 选择的运算符
		# 清空运算符显示
		self.lb_ops_selected.setText("")    # 运算符显示
		self.p1_selected.setStyleSheet("border-image: none;") # 清空已选牌面

		self.pushButton_ast.setEnabled(True)
		self.pushButton_allan.setEnabled(True)
		self.pushButton_dtree.setEnabled(True)
		self.pushButton_autosv.setEnabled(True)




	def select_ops(self, ops):
		self.lb_ops_selected.setText(ops)
		self.selected_ops = ops

	def add_value_node(self):
		# self.scene.clear()
		if self.selected_value is not None:  # 选择了卡片值
			self.value_nodes.append(self.selected_value)  # 添加到节点数值列表
			for i in range(len(self.adds_id) + 1):
				if i not in self.adds_id:
					value = self.selected_value
					nodet = RoundedRectangleItem(150 * self.seq, 0, str(value), i)
					self.seq += 1  # 序号加1
					nodet.selected.connect(lambda id: self.selected_node(id))  # 连接信号
					nodet.unselected.connect(lambda id: self.unselected_node(id))
					node = data.Node([i, 'Value', None, None, None, value, None])
					self.adds_id.add(i)  # 添加全局id
					self.TreeNode.append(node)
					self.scene.addItem(nodet)
		else:
			QMessageBox.warning(self, '警告', '请先选择一张牌')

	def judge_node_conn(self):  # 判断节点是否已经连接
		for id in self.selected_id:
			for node in self.TreeNode:
				if node.LeftNodeID == id or node.RightNodeID == id:
					return True

	def add_operator_node(self):  # 添加运算符节点
		if self.selected_ops is not None:
			if len(self.selected_id) == 2:
				# 判断已选节点是否已经被连接
				if self.judge_node_conn():
					QMessageBox.warning(self, '警告', '节点已经连接')
					return
				for i in range(len(self.adds_id) + 1):  # 确保id唯一
					if i not in self.adds_id:
						node = data.Node([i, 'Operator', self.selected_ops, None, None, None, None])
						self.TreeNode.append(node)
						self.adds_id.add(i)  # 全局ID
						node.LeftNodeID = int(self.selected_id[1])
						node.RightNodeID = int(self.selected_id[0])
						self.flash_node(self.selected_id[0])
						self.flash_node(self.selected_id[1])

						self.selected_id = []  # 清空选中的节点
						self.trees_root.append(node)  # 保存根节点
						# 从根节点列表中删去作为子节点连接的节点
						node_to_remove = []
						for root in self.trees_root:
							if root.NodeID == node.LeftNodeID or root.NodeID == node.RightNodeID:
								node_to_remove.append(root)
						for root in node_to_remove:
							self.trees_root.remove(root)

						print("已用ID", self.adds_id)
						self.scene.clear()
						for i, root in enumerate(self.trees_root):
							if self.has_both_operators(self.TreeNode):
								self.offset = 35
							self.ShowTree(root, self.TreeNode, i * 300, 0)
							self.offset = 0

						self.seq = 1  # 重置序号
						for noed in self.TreeNode:
							print("运算符节点", noed)
						# exps = data.get_expression(self.TreeNode)
						# print("表达式", exps)
			else:
				QMessageBox.warning(self, '警告', '请先选择两个节点')
		else:
			QMessageBox.warning(self, '警告', '请先选择一个运算符')

	# 根据节点ID，找到对应的节点
	def find_node(self, id):
		for node in self.TreeNode:
			if node.NodeID == id:
				return node
	def exchange_value_node(self):
		# 修改节点值
		if len(self.selected_id) == 1:
			node = self.find_node(self.selected_id[0])
			if node.NodeType == 'Operator':
				QMessageBox.warning(self, '警告', '请选择数值节点')
				return
			if self.selected_value:
				if self.TreeNode:
					for node in self.TreeNode:
						if node.NodeID == self.selected_id[0]:
							node.FaceValue = str(self.selected_value)
							for nodet in self.scene.items():
								if isinstance(nodet, RoundedRectangleItem):
									if nodet.id == self.selected_id[0]:
										nodet.text = str(self.selected_value)
										nodet.update()
			else:
				QMessageBox.warning(self, '警告', '请先选择一张牌')
		else:
			QMessageBox.warning(self, '警告', '请先选择一个节点')

	def exchange_ops_node(self):
		# 修改节点运算符
		if len(self.selected_id) == 1:
			node = self.find_node(self.selected_id[0])
			if node.NodeType == 'Value':
				QMessageBox.warning(self, '警告', '请选择运算符节点')
				return
			if self.selected_ops:
				if self.TreeNode:
					for node in self.TreeNode:
						if node.NodeID == self.selected_id[0]:
							node.Ops = self.selected_ops
							for nodet in self.scene.items():
								if isinstance(nodet, RoundedRectangleItem):
									if nodet.id == self.selected_id[0]:
										nodet.text = str(self.selected_ops)
										nodet.update()
			else:
				QMessageBox.warning(self, '警告', '请先选择一个运算符')
		else:
			QMessageBox.warning(self, '警告', '请先选择一个节点')

	def exchange_two_nodes(self):
		# 交换选中两个节点FaceValue的值
		if len(self.selected_id) == 2:
			if self.TreeNode:
				node_1 = None
				node_2 = None
				for node in self.TreeNode:
					if node.NodeID == self.selected_id[0]:
						if node.NodeType == 'Value':
							node_1 = node
						else:
							QMessageBox.warning(self, '警告', '请选择两个数值节点')
							return
					elif node.NodeID == self.selected_id[1]:
						if node.NodeType == 'Value':
							node_2 = node
						else:
							QMessageBox.warning(self, '警告', '请选择两个数值节点')
							return
				if node_1 and node_2:
					node_1.FaceValue, node_2.FaceValue = node_2.FaceValue, node_1.FaceValue
					for item in self.scene.items():
						if isinstance(item, RoundedRectangleItem):
							if item.id == self.selected_id[0]:
								item.text = str(node_1.FaceValue)
								item.update()
							elif item.id == self.selected_id[1]:
								item.text = str(node_2.FaceValue)
								item.update()
		else:
			QMessageBox.warning(self, '警告', '请选择两个节点')

	def selected_node(self, id):
		if len(self.selected_id) < 2:
			self.selected_id.append(id)
		else:
			QMessageBox.warning(self, '警告', '不能选择超过3个节点')
			for nodet in self.scene.items():  # 遍历场景中的所有图元
				if isinstance(nodet, RoundedRectangleItem):  # 如果是圆角矩形
					if nodet.id == id:  # 如果是当前选中的节点
						nodet.isSelected = False  # 取消选中
						nodet.update()  # 更新图元

	def unselected_node(self, id):
		self.selected_id.remove(id)
		print(self.selected_id)

	def flash_node(self, id):  # 还原节点为未选中状态
		for nodet in self.scene.items():  # 遍历场景中的所有图元
			if isinstance(nodet, RoundedRectangleItem):  # 如果是圆角矩形
				if nodet.id == id:  # 如果是当前选中的节点
					nodet.isSelected = False  # 取消选中
					nodet.update()  # 更新图元

	def init_ui(self):
		self.scene = QGraphicsScene()
		self.graphicsView.setScene(self.scene)

	def init_slot(self):
		self.pushButton.clicked.connect(self.on_calc)

	def load_image(self, path, Tree_4_nums, index):
		def current_selection(value, path):
			self.selected_value = str(value)
			self.p1_selected.setStyleSheet(f"border-image: url({path});")
			self.pushButton_ast.setEnabled(False)
			self.pushButton_allan.setEnabled(False)
			self.pushButton_dtree.setEnabled(False)
			self.pushButton_autosv.setEnabled(False)
			# print(value)

		if index == 0:
			self.p1.setStyleSheet(f"border-image: url({path});")
			self.p1.clicked.connect(lambda: current_selection(Tree_4_nums[index], path))
		elif index == 1:
			self.p2.setStyleSheet(f"border-image: url({path});")
			self.p2.clicked.connect(lambda: current_selection(Tree_4_nums[index], path))
		elif index == 2:
			self.p3.setStyleSheet(f"border-image: url({path});")
			self.p3.clicked.connect(lambda: current_selection(Tree_4_nums[index], path))
		elif index == 3:
			self.p4.setStyleSheet(f"border-image: url({path});")
			self.p4.clicked.connect(lambda: current_selection(Tree_4_nums[index], path))

	def show_card(self):
		# 原始数组
		original_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
		face_card = ['Club', 'Diamond', 'Heart', 'Spade']
		# 从原始数组中随机选择4个元素组成新列表
		self.Tree_4_nums = random.sample(original_list, 4)
		# 从花色中随机选择一个
		for index, num in enumerate(self.Tree_4_nums):
			card_face = random.choice(face_card)
			if num == 11:
				card_path = f"card/{card_face}/{card_face}j.png"
			elif num == 12:
				card_path = f"card/{card_face}/{card_face}q.png"
			elif num == 13:
				card_path = f"card/{card_face}/{card_face}k.png"
			else:
				card_path = f"card/{card_face}/{card_face}{num}.png"
			self.load_image(card_path, self.Tree_4_nums, index)
		# return self.Tree_4_nums

	def question_card(self):    # 显示题目卡片
		self.secen_clear()  # 清空画布
		self.lineEdit.clear()
		self.label_result.setText(" =? ")
		self.label_TF.setText("")
		self.show_card()  # 显示卡片

	# 换牌

	def change_card(self):
		self.show_card()  # 显示卡片


	def AutoTest(self):
		if self.Tree_4_nums is None:
			QMessageBox.warning(self, '警告', '请先出题')
			return
		else:
			results = data.get_Aexps(self.Tree_4_nums)
			if results == "无法得出24点":
				self.lineEdit.clear()
				self.lineEdit.setText("无法得出24点")
				return
			else:
				if self.r_count < len(results):
					self.lineEdit.clear()
					self.lineEdit.setText(f"{results[self.r_count]}")
					self.on_calc()  # 自动计算
					self.draw_tree()  # 绘制语法树
					self.r_count += 1
				else:
					QMessageBox.information(self, '提示', '已经是最后一个表达式了')
					self.r_count = 0

	def AutoTest_9_10(self):
		if self.Tree_4_nums is None:
			QMessageBox.warning(self, '警告', '请先出题')
			return
		else:
			aexp = data.gen_exp(self.Tree_4_nums)
			if aexp == "无法得出24点":
				self.lineEdit.clear()
				self.lineEdit.setText("无法得出24点")
			else:
				self.lineEdit.clear()
				self.lineEdit.setText(f"{aexp}")
				self.on_calc()  # 自动计算
				self.draw_tree()  # 绘制语法树

	def calculate_button(self):  # 计算生成语法树
		self.scene.clear()
		aexp = self.lineEdit.text()
		try:
			exp_ls = data.get_postfix_expression(aexp)  # 获取后缀表达式列表
			self.TreeNode = data.ExpressionAnalyse(exp_ls)  # 将后缀表达式转化为节点树
			result = data.calculate(self.TreeNode[-1], self.TreeNode)
			self.label_result.setText(f"={round(result, 2)}")
		except:
			QMessageBox.warning(self, '警告', '表达式错误！')
			return
		if self.TreeNode:   # 如果节点树不为空
			root = self.TreeNode[-1]
			result = data.calculate(root, self.TreeNode)    # 计算表达式的值
			self.label_result.setText(f"={round(result, 2)}")
			self.result_ToF(int(result))    # 判断结果是否为24
			if self.has_both_operators(self.TreeNode):
				self.offset = 35
			self.ShowTree(root, self.TreeNode)
			self.offset = 0

	def has_both_operators(self, TreeNode):  # 判断是否同时有两个运算符
		ops = data.OPS

		def find_op_by_id(id):  # 根据ID查找运算符
			for node in TreeNode:
				if node.NodeID == id and node.NodeType == "Operator":
					return node.Ops

		for node in TreeNode:  # 遍历节点树
			if node.NodeType == "Operator":
				opl = find_op_by_id(node.LeftNodeID)
				opr = find_op_by_id(node.RightNodeID)
				if opl in ops and opr in ops:
					return True

	def check_tree_and_expression(self):  # 检查节点树和表达式是否一致
		if self.TreeNode is not None:
			aexp = self.lineEdit.text()
			if aexp == '':
				QMessageBox.warning(self, '警告', '请输入表达式')
				return
			try:
				exp_ls = data.get_postfix_expression(aexp)  # 获取后缀表达式列表
				TreeNode2 = data.ExpressionAnalyse(exp_ls)  # 将后缀表达式转化为节点树
				if self.check_tree(self.TreeNode, TreeNode2):
					QMessageBox.information(self, '提示', '节点树与表达式一致')
				else:
					QMessageBox.warning(self, '警告', '节点树与表达式不一致')
			except Exception as e:
				QMessageBox.warning(self, '警告', '表达式错误')
				return
		else:
			QMessageBox.warning(self, '警告', '当前无语法树')


	# 检查两个节点树是否一致的函数

	def check_tree(self, TreeNode1, TreeNode2):
		if len(TreeNode1) != len(TreeNode2):
			return False
		for i in range(len(TreeNode1)):
			if TreeNode1[i].NodeID != TreeNode2[i].NodeID:
				return False
			if TreeNode1[i].NodeType != TreeNode2[i].NodeType:
				return False
			if TreeNode1[i].Ops != TreeNode2[i].Ops:
				return False
			if TreeNode1[i].FaceValue != TreeNode2[i].FaceValue:
				return False
			if TreeNode1[i].LeftNodeID != TreeNode2[i].LeftNodeID:
				return False
			if TreeNode1[i].RightNodeID != TreeNode2[i].RightNodeID:
				return False
		return True



	def draw_tree(self):
		self.scene.clear()
		aexp = self.lineEdit.text()
		if aexp == "":
			QMessageBox.warning(self, '警告', '请输入表达式')
			return
		try:
			exp_ls = data.get_postfix_expression(aexp)  # 获取后缀表达式列表
			self.TreeNode = data.ExpressionAnalyse(exp_ls)  # 将后缀表达式转化为节点树
			root = self.TreeNode[-1]
			if self.has_both_operators(self.TreeNode):
				self.offset = 35
			self.ShowTree(root, self.TreeNode, depth=0)
			self.offset = 0
		except Exception as e:
			QMessageBox.warning(self, '警告', '表达式错误')

	def ShowTree(self, root, TreeNode, startx=0, starty=0, depth=0):
		self.selected_id = []  # 清空选中的节点
		self.drawn_tree_id = set()  # 清空已绘制的节点ID
		# 绘制根节点
		if root.NodeType == 'Value':
			nds_root = RoundedRectangleItem(startx, starty, root.FaceValue, root.NodeID)
			nds_root.selected.connect(lambda id: self.selected_node(id))
			nds_root.unselected.connect(lambda id: self.unselected_node(id))
			self.drawn_tree_id.add(root.NodeID)
			self.adds_id.add(root.NodeID)
		else:
			nds_root = RoundedRectangleItem(startx, starty, root.Ops, root.NodeID)
			nds_root.selected.connect(lambda id: self.selected_node(id))
			nds_root.unselected.connect(lambda id: self.unselected_node(id))
			self.drawn_tree_id.add(root.NodeID)
			self.adds_id.add(root.NodeID)
		nds_root.setZValue(1)
		self.scene.addItem(nds_root)

		# 计算节点间的水平偏移量
		horizontal_offset = 100 - depth * self.offset

		# 计算左子树和右子树的位置
		left_x, left_y = startx - horizontal_offset, starty + 100
		right_x, right_y = startx + horizontal_offset, starty + 100

		# 绘制左子树
		if root.LeftNodeID is not None:
			left_child = next(node for node in TreeNode if node.NodeID == root.LeftNodeID)
			self.ShowTree(left_child, TreeNode, left_x, left_y, depth + 1)

			# 绘制左子树与根节点之间的连线
			line_left = QGraphicsLineItem(startx + 35, starty + 35, left_x + 35, left_y + 35)
			line_left.setZValue(-1)
			line_left.setPen(QPen(Qt.red, 3))
			self.scene.addItem(line_left)

		# 绘制右子树
		if root.RightNodeID is not None:
			right_child = next(node for node in TreeNode if node.NodeID == root.RightNodeID)
			self.ShowTree(right_child, TreeNode, right_x, right_y, depth + 1)

			# 绘制右子树与根节点之间的连线
			line_right = QGraphicsLineItem(startx + 35, starty + 35, right_x + 35, right_y + 35)
			line_right.setZValue(-1)
			line_right.setPen(QPen(Qt.red, 3))
			self.scene.addItem(line_right)

	def on_calc(self):
		aexp = self.lineEdit.text()
		if aexp == '':
			QMessageBox.warning(self, '警告', '请输入表达式')
			return
		try:
			result = eval(aexp)
			self.label_result.setText(f"={int(result)}")
			# 设置字体样式
			self.result_ToF(result)
		except Exception as e:
			QMessageBox.warning(self, '警告', '表达式错误')
			return

	def result_ToF(self, result):
		font = QFont('Microsoft YaHei', 16)
		font.setBold(True)  # 将字体设置为粗体
		self.label_TF.setText('√' if result == 24 else '×')
		# 将字体样式应用到 QLabel 上
		self.label_TF.setFont(font)
		# 将文本颜色设置为红色
		self.label_TF.setStyleSheet("color: red;background-color:rgb(219, 205, 166)")

	def agent_exp(self):
		if len(self.TreeNode) > 0:
			exp_str = data.get_expression(self.TreeNode)
			if exp_str is not None:
				self.label_result.clear()   # 清空显示结果
				self.lineEdit.clear()   # 清空显示表达式
				self.lineEdit.setText(exp_str)   # 显示表达式
			else:
				QMessageBox.warning(self, '警告', '无法生成表达式')
		else:
			QMessageBox.warning(self, '警告', '当前无语法树')


if __name__ == '__main__':
	app = QApplication()
	window = MainWindow()
	window.show()
	app.exec()
