package com.imyeyu.fx.ui.components.popup;

import com.imyeyu.fx.ui.TimiFXUI;
import com.imyeyu.fx.ui.components.popup.tips.AbstractPopupTips;
import com.imyeyu.java.bean.CallbackArg;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.stage.Popup;

import java.awt.MouseInfo;
import java.awt.Point;

/**
 * 抽象弹出提示服务，你需要实现 {@link #createRoot()}，为 Popup 提供标准根节点。
 *
 * @author 夜雨
 * @since 2021-12-04 14:26
 */
public abstract class AbstractPopupTipsService<T extends Pane> extends Popup implements TimiFXUI {

	/**
	 * 组件安装提示后会把提示对象（{@link AbstractPopupTips}）添加到 {@link Node#getProperties()} 中。
	 * 可以通过此 KEY 获取该对象
	 */
	protected static final String TIPS_KEY = "TIMI_FX_POPUP_TIPS";

	/** 根布局 */
	protected T root;

	/** 显示到跟布局中 */
	protected CallbackArg<Node> showOnRoot;

	/** 显示提示的节点 */
	protected final ObjectProperty<Node> showingTipsNode;

	/** 默认构造 */
	protected AbstractPopupTipsService() {
		showingTipsNode = new SimpleObjectProperty<>();
		showingTipsNode.addListener((obs, o, newNode) -> {
			if (newNode != null) {
				if (newNode.getProperties().get(TIPS_KEY) instanceof AbstractPopupTips<?> tips) {
					if (showOnRoot == null) {
						root.getChildren().setAll(tips.getNode());
					} else {
						showOnRoot.handler(tips.getNode());
					}
				}
			}
		});

		root = createRoot();

		getContent().add(root);
		setAutoHide(true);
		getScene().setFill(null);

		// ---------- 事件 ----------

		setOnHidden(e -> showingTipsNode.set(null));
		// 显示提示监听
		showingTipsNode.addListener((obs, oldNode, newNode) -> {
			autoHideProperty().unbind();
			if (newNode != null) {
				Object newObj = newNode.getProperties().get(TIPS_KEY);
				if (newObj instanceof AbstractPopupTips<?> newTips) {
					if (newTips.isEnable()) {
						// 已启用
						autoHideProperty().bind(newTips.keepShowProperty());
						// 显示
						Point p = MouseInfo.getPointerInfo().getLocation();
						if (!isShowing()) {
							super.show(newNode.getScene().getWindow(), p.x + 16, p.y + 12);
						}
						setOpacity(1);
					}
				}
			}
			if (oldNode != null) {
				Object oldObj = oldNode.getProperties().get(TIPS_KEY);
				if (oldObj instanceof AbstractPopupTips<?> oldTips) {
					setOpacity(0);
					oldTips.setKeepShow(false);
				}
			}
		});
	}

	/**
	 * 构造根容器
	 *
	 * @return 根容器
	 */
	protected abstract T createRoot();

	/**
	 * 强制显示并保持
	 *
	 * @param node 安装了提示的组件
	 */
	public void showAndKeep(Node node) {
		Object obj = node.getProperties().get(TIPS_KEY);
		if (obj instanceof AbstractPopupTips<?> tips) {
			tips.setKeepShow(true);
			showingTipsNode.set(node);
		}
	}

	/**
	 * 为组件安装弹窗提示
	 *
	 * @param node      组件
	 * @param popupTips 弹窗数据
	 */
	public void install(Node node, AbstractPopupTips<?> popupTips) {
		node.getProperties().put(TIPS_KEY, popupTips);
		// 指向事件
		node.hoverProperty().addListener((obs, o, isHover) -> {
			if (isHover) {
				showingTipsNode.set(node);
			} else {
				if (!popupTips.isKeepShow()) {
					showingTipsNode.set(null);
				}
			}
		});
		// 移动事件
		EventHandler<MouseEvent> mouseEvent = e -> {
			if (!popupTips.isKeepShow()) {
				Bounds nodeBounds = node.getLayoutBounds();
				if (!popupTips.isEnable() || e.getX() < 0 || nodeBounds.getWidth() < e.getX() || e.getY() < 0 || nodeBounds.getHeight() < e.getY()) {
					// 提示没有开启或光标移出组件
					showingTipsNode.set(null);
				} else {
					showingTipsNode.set(node);
					if (isShowing()) {
						setX(e.getScreenX() + 12);
						setY(e.getScreenY() + 8);
					}
				}
			}
		};
		node.addEventFilter(MouseEvent.MOUSE_MOVED, mouseEvent);
		node.addEventFilter(MouseEvent.MOUSE_DRAGGED, mouseEvent);
	}
}
