using Log;

namespace Modules.BehaviorTree
{
	public static class BehaviorTreeLayout
	{
		private const double XGap = 20;
		private const double YGap = 10;
		private static double rootOrigX;
		private static double rootOrigY;
		private static double rootOffsetX;
		private static double rootOffsetY;

		private static TreeNodeViewModel LeftMostOffspring(
			TreeNodeViewModel treeNode, int currentLevel, int searchLevel)
		{
			if (currentLevel == searchLevel)
			{
				return treeNode;
			}
			for (int i = 0; i < treeNode.Children.Count; ++i)
			{
				var child = treeNode.Children[i];
				child.AncestorModify = treeNode.Modify + treeNode.AncestorModify;
				var offspring = LeftMostOffspring(child, currentLevel + 1, searchLevel);
				if (offspring == null)
				{
					continue;
				}
				return offspring;
			}
			return null;
		}

		private static TreeNodeViewModel RightMostOffspring(
			TreeNodeViewModel treeNode, int currentLevel, int searchLevel)
		{
			if (currentLevel == searchLevel)
			{
				return treeNode;
			}
			for (int i = treeNode.Children.Count - 1; i >= 0; --i)
			{
				var child = treeNode.Children[i];
				child.AncestorModify = treeNode.Modify + treeNode.AncestorModify;
				var offspring = RightMostOffspring(child, currentLevel + 1, searchLevel);
				if (offspring == null)
				{
					continue;
				}
				return offspring;
			}
			return null;
		}

		private static void AjustSubTreeGap(TreeNodeViewModel left, TreeNodeViewModel right)
		{
			double offset = 0;
			TreeNodeViewModel tLeft = left;
			TreeNodeViewModel tRight = right;
			left.AncestorModify = 0;
			right.AncestorModify = 0;
			for (int i = 0; tLeft != null && tRight != null; ++i)
			{
				double tGap = (tRight.Prelim + tRight.AncestorModify) - (tLeft.Prelim + tLeft.AncestorModify);
				if (XGap + TreeNodeViewModel.Width - tGap > offset)
				{
					offset = XGap + TreeNodeViewModel.Width - tGap;
				}
				tLeft = RightMostOffspring(left, 0, i + 1);
				tRight = LeftMostOffspring(right, 0, i + 1);
			}
			right.Modify += offset;
			right.Prelim += offset;
		}

		private static void AjustTreeGap(TreeNodeViewModel treeNode)
		{
			for (int i = 0; i < treeNode.Children.Count - 1; ++i)
			{
				for (int j = i + 1; j < treeNode.Children.Count; ++j)
				{
					var left = treeNode.Children[i];
					var right = treeNode.Children[j];
					AjustSubTreeGap(left, right);
				}
			}
		}

		private static void CalculatePrelimAndModify(TreeNodeViewModel treeNode)
		{
			foreach (TreeNodeViewModel node in treeNode.Children)
			{
				CalculatePrelimAndModify(node);
			}

			double prelim = 0;
			double modify = 0;

			if (treeNode.IsLeaf)
			{
				if (treeNode.LeftSibling == null)
				{
					// 如果没有左邻居，不需要设置modify
					prelim = 0;
				}
				else
				{
					prelim = treeNode.LeftSibling.Prelim + TreeNodeViewModel.Width + XGap;
				}
			}
			else
			{
				// 调整子树间的间距
				AjustTreeGap(treeNode);
				double childrenCenter = (treeNode.FirstChild.Prelim + treeNode.LastChild.Prelim) / 2;
				if (treeNode.LeftSibling == null)
				{
					// 如果没有左邻居，不需要设置modify
					prelim = childrenCenter;
				}
				else
				{
					prelim = treeNode.LeftSibling.Prelim + TreeNodeViewModel.Width + XGap;
					modify = prelim - childrenCenter;
				}
			}
			treeNode.Prelim = prelim;
			treeNode.Modify = modify;

			Logger.Debug("Num: " + treeNode.Num + " Prelim: " + treeNode.Prelim + " Modify: " +
				treeNode.Modify);
		}

		private static void CalculateRelativeXAndY(
			TreeNodeViewModel treeNode, int level, double totalModify)
		{
			foreach (TreeNodeViewModel node in treeNode.Children)
			{
				CalculateRelativeXAndY(node, level + 1, treeNode.Modify + totalModify);
			}
			if (treeNode.IsLeaf)
			{
				treeNode.X = treeNode.Prelim + totalModify;
			}
			else
			{
				treeNode.X = (treeNode.FirstChild.X + treeNode.LastChild.X) / 2;
			}
			treeNode.Y = level * (TreeNodeViewModel.Height + YGap);
		}

		private static void FixXAndY(TreeNodeViewModel treeNode)
		{
			treeNode.X += rootOffsetX;
			treeNode.Y += rootOffsetY;
			foreach (var node in treeNode.Children)
			{
				FixXAndY(node);
			}
		}

		public static void ExcuteLayout(TreeNodeViewModel root)
		{
			if (root == null)
			{
				return;
			}
			rootOrigX = root.X;
			rootOrigY = root.Y;
			CalculatePrelimAndModify(root);
			CalculateRelativeXAndY(root, 0, 0);

			rootOffsetX = rootOrigX - root.X;
			rootOffsetY = rootOrigY - root.Y;
			FixXAndY(root);
		}
	}
}