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

import com.imyeyu.fx.ui.TimiFXUI;
import com.imyeyu.fx.utils.Windows;
import com.imyeyu.fx.utils.screen.Screens;
import com.imyeyu.java.bean.CallbackArg;
import com.imyeyu.java.bean.CallbackArgReturn;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Modality;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import lombok.Getter;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;

/**
 * 抽象弹窗
 *
 * @author 夜雨
 * @since 2022-01-07 09:24
 */
public abstract class AbstractAlert extends Stage implements TimiFXUI, TimiFXUI.Colorful {

	/** 默认按钮边距 */
	protected static final Insets PADDING_BUTTON  = new Insets(12, 16, 12, 16);

	/** 默认内容边距 */
	protected static final Insets PADDING_CONTENT = new Insets(8, 16, 8, 16);

	/** 按钮布局面板的左侧面板（如果按钮布局主面板被修改，此面板无效） */
	@Getter
	protected final HBox leftButtons;

	/** 按钮布局面板的中间面板（如果按钮布局主面板被修改，此面板无效） */
	@Getter
	protected final HBox centerButtons;

	/** 按钮布局面板的右侧面板（如果按钮布局主面板被修改，此面板无效） */
	@Getter
	protected final HBox rightButtons;

	/** 根布局（BorderPane 下部分为按钮面板） */
	@Getter
	protected final BorderPane root;

	/** 按钮面板，{@link #leftButtons}、{@link #centerButtons} 、{@link #rightButtons} 在此面板中 */
	@Getter
	protected final BorderPane btnPane;

	private final ObjectProperty<AlertType> typeProperty;
	private final List<CallbackArg<WindowEvent>> shownListeners;

	/** 最近用户动作 */
	@Getter
	private AlertButton.Action action;

	/** 弹窗动作事件 */
	@Setter
	private CallbackArgReturn<AlertButton.Action, Boolean> onActionEvent;

	/** true 为窗体尺寸适应场景尺寸 */
	@Setter
	@Getter
	protected boolean enableSizeToScene = true;

	/** 默认构造 */
	public AbstractAlert() {
		typeProperty = new SimpleObjectProperty<>();
		shownListeners = new ArrayList<>();
		action = AlertButton.Action.CANCEL; // 默认取消

		// 按钮布局
		leftButtons = new HBox(6);
		centerButtons = new HBox(6);
		rightButtons = new HBox(6);

		leftButtons.setAlignment(Pos.CENTER_LEFT);
		centerButtons.setAlignment(Pos.CENTER);
		rightButtons.setAlignment(Pos.CENTER_RIGHT);

		// 根布局
		root = new BorderPane();
		root.setBorder(Stroke.TOP);
		root.setBottom(btnPane = new BorderPane() {{
			setPadding(PADDING_BUTTON);
			setLeft(leftButtons);
			setCenter(centerButtons);
			setRight(rightButtons);
		}});

		Scene scene = new Scene(root);
		TimiFXUI.applyStyle(scene);
		initModality(Modality.WINDOW_MODAL);
		setScene(scene);

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

		// 显示
		setOnShown(e -> {
			if (enableSizeToScene) {
				sizeToScene();
			}
			callShownListeners(e);
		});
		// 类型变更
		typeProperty.addListener((obs, o, newType) -> {
			if (newType != null) {
				getIcons().setAll(newType.icon);
				setTitle(newType.title);
			}
		});

		layout(root);

		addEventFilter(KeyEvent.KEY_RELEASED, e -> {
			boolean control = e.isControlDown();
			boolean shift = e.isShiftDown();
			boolean alt = e.isAltDown();
			KeyCode code = e.getCode();

			if (!control && !shift && !alt) {
				if (code == KeyCode.ESCAPE) {
					onEscape();
				}
			}
		});
	}

	/**
	 * 回调显示监听
	 *
	 * @param e 显示事件
	 */
	private void callShownListeners(WindowEvent e) {
		for (int i = 0; i < shownListeners.size(); i++) {
			shownListeners.get(i).handler(e);
		}
	}

	/** 默认 ESC 键关闭 */
	protected void onEscape() {
		close();
	}

	/**
	 * 方便匿名内部类的布局完成回调
	 *
	 * @param root 根布局
	 */
	protected void layout(BorderPane root) {
		// 子类实现
	}

	/**
	 * 自适应窗体尺寸
	 *
	 * @return 本实例
	 */
	public AbstractAlert autoSize() {
		sizeToScene();
		return this;
	}

	/**
	 * 相对居中显示，不越出父级标题
	 *
	 * @param owner 父级窗体
	 */
	public void showRelativeCenter(Window owner) {
		if (getOwner() == null) {
			initOwner(owner);
		}
		setOnShown(e -> {
			Windows.centerOnOwner(owner, this);
			callShownListeners(e);
		});
		show();
	}

	/**
	 * 相对居中显示并等待，不越出父级标题
	 *
	 * @param owner 父级窗体
	 */
	public void showAwaitRelativeCenter(Window owner) {
		if (getOwner() == null) {
			initOwner(owner);
		}
		setOnShown(e -> {
			Windows.centerOnOwner(owner, this);
			callShownListeners(e);
		});
		showAndWait();
	}

	/** 相对于主屏幕中间显示 */
	public void showRelativeCenter4PrimaryScreen() {
		showRelativeCenter4Screen(Screens.primary());
	}

	/**
	 * 相对于屏幕中间显示
	 *
	 * @param screen 屏幕
	 */
	public void showRelativeCenter4Screen(Screen screen) {
		initModality(Modality.APPLICATION_MODAL);
		setOnShown(e -> {
			Windows.centerOnScreen(this, screen);
			callShownListeners(e);
		});
		show();
	}

	/** 相对于主屏幕中间显示并等待 */
	public void showAwaitRelativeCenter4PrimaryScreen() {
		showAwaitRelativeCenter4Screen(Screens.primary());
	}

	/**
	 * 相对于屏幕中间显示并等待
	 *
	 * @param screen 屏幕
	 */
	public void showAwaitRelativeCenter4Screen(Screen screen) {
		initModality(Modality.APPLICATION_MODAL);
		setOnShown(e -> {
			Windows.centerOnScreen(this, screen);
			callShownListeners(e);
		});
		showAndWait();
	}

	/** 清除所有按钮 */
	public void clearButton() {
		leftButtons.getChildren().clear();
		centerButtons.getChildren().clear();
		rightButtons.getChildren().clear();
	}

	/**
	 * 设置弹窗按钮，使用按钮默认位置
	 *
	 * @param buttons 弹窗按钮
	 */
	public void setButton(AlertButton... buttons) {
		clearButton();
		putButtons(buttons);
	}

	/**
	 * 追加弹窗按钮，使用按钮默认位置
	 *
	 * @param buttons 弹窗按钮
	 */
	public void putButtons(AlertButton... buttons) {
		if (buttons != null) {
			for (int i = 0; i < buttons.length; i++) {
				final int j = i;
				buttons[i].setOnAction(e -> {
					action = buttons[j].action;
					if (onActionEvent == null || onActionEvent.handler(buttons[j].action)) {
						close();
					}
				});
				switch (buttons[i].pos) {
					case LEFT   -> leftButtons.getChildren().add(buttons[i]);
					case CENTER -> centerButtons.getChildren().add(buttons[i]);
					case RIGHT  -> rightButtons.getChildren().add(buttons[i]);
				}
			}
		}
	}

	/**
	 * 追加弹窗按钮
	 *
	 * @param to   目标容器
	 * @param buttons 弹窗按钮
	 */
	public void putButtons(HBox to, AlertButton... buttons) {
		for (int i = 0; i < buttons.length; i++) {
			final int j = i;
			buttons[i].setOnAction(e -> {
				action = buttons[j].action;
				if (onActionEvent == null || onActionEvent.handler(buttons[j].action)) {
					close();
				}
			});
			to.getChildren().add(buttons[i]);
		}
	}

	/**
	 * 设置左侧弹窗按钮
	 *
	 * @param buttons 按钮
	 */
	public void setLeftButtons(AlertButton... buttons) {
		leftButtons.getChildren().clear();
		putButtons(leftButtons, buttons);
	}

	/**
	 * 设置中间弹窗按钮
	 *
	 * @param buttons 弹窗按钮
	 */
	public void setCenterButtons(AlertButton... buttons) {
		centerButtons.getChildren().clear();
		putButtons(centerButtons, buttons);
	}

	/**
	 * 设置右侧弹窗按钮
	 *
	 * @param buttons 弹出按钮
	 */
	public void setRightButtons(AlertButton... buttons) {
		rightButtons.getChildren().clear();
		putButtons(rightButtons, buttons);
	}

	/**
	 * 设置弹窗类型
	 *
	 * @param type 弹窗类型
	 */
	public void setType(AlertType type) {
		this.typeProperty.set(type);
	}

	/**
	 * 获取弹窗类型
	 *
	 * @return 弹窗类型
	 */
	public AlertType getType() {
		return typeProperty.get();
	}

	/**
	 * 获取弹窗类型监听
	 *
	 * @return 弹窗类型监听
	 */
	public ObjectProperty<AlertType> typeProperty() {
		return typeProperty;
	}

	/**
	 * 设置图标
	 *
	 * @param icon 图标
	 */
	public void setIcon(Image icon) {
		getIcons().setAll(icon);
	}

	/**
	 * 添加显示回调
	 *
	 * @param callback 回调
	 */
	public void addShownListener(CallbackArg<WindowEvent> callback) {
		shownListeners.add(callback);
	}
}
