package com.imyeyu.fx.ui.components;

import com.imyeyu.fx.icon.TimiFXIcon;
import com.imyeyu.fx.icon.TimiIcon;
import com.imyeyu.fx.task.RunAsync;
import com.imyeyu.fx.ui.TimiFXUI;
import com.imyeyu.fx.ui.components.alert.AlertButton;
import com.imyeyu.fx.ui.components.alert.AlertConfirm;
import com.imyeyu.fx.ui.components.alert.AlertTextField;
import com.imyeyu.fx.ui.components.alert.AlertTips;
import com.imyeyu.fx.utils.Observables;
import com.imyeyu.io.IO;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.CallbackArgReturn;
import com.imyeyu.utils.OS;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.text.Text;

import javax.naming.NoPermissionException;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 文件目录树组件
 *
 * @author 夜雨
 * @since 2022-05-26 14:32
 */
public class FileTreeView extends TreeView<File> implements TimiFXUI.Colorful {

	/** 正在查找节点监听 */
	protected final BooleanBinding findingItem;

	/** 显示隐藏文件监听 */
	protected final BooleanProperty showHide;

	/** 选择队列，左进左出，右边为深度路径，不为空时表正在查找节点 */
	protected final ObservableList<File> selectDeque = FXCollections.synchronizedObservableList(FXCollections.observableArrayList());

	/** 过滤列表，返回 true 时创建该节点 */
	protected final List<CallbackArgReturn<File, Boolean>> itemFilters;

	/** 默认构造器 */
	public FileTreeView() {
		showHide = new SimpleBooleanProperty(false);
		itemFilters = new ArrayList<>();
		findingItem = Bindings.isNotEmpty(selectDeque);

		disableProperty().bind(findingItem);
		setCellFactory(cell -> new TreeCell<>() {

			final Label loading = new Label(TimiFXUI.MULTILINGUAL.text("loading"));
			final Text iconFile = TimiFXIcon.get(TimiIcon.FILE);
			final Text iconDirectory = TimiFXIcon.get(TimiIcon.FOLDER);

			{
				iconFile.fillProperty().bind(Bindings.when(FileTreeView.this.focusedProperty().and(selectedProperty())).then(GRAY_WHITE).otherwise(GRAY));
				iconDirectory.fillProperty().bind(Bindings.when(FileTreeView.this.focusedProperty().and(selectedProperty())).then(GRAY_WHITE).otherwise(GRAY));
			}

			@Override
			protected void updateItem(File file, boolean empty) {
				super.updateItem(file, empty);
				if (empty) {
					setText("");
					setGraphic(null);
				} else {
					if (file == null) {
						setGraphic(loading);
					} else {
						if (TimiJava.isEmpty(file.getName())) {
							setText(file.toString());
						} else {
							setText(file.getName());
						}
						setGraphic(file.isFile() ? iconFile : iconDirectory);
					}
				}
			}
		});

		// 右键菜单
		MenuItem menuMkdir = new MenuItem(TimiFXUI.MULTILINGUAL.text("file.mkdir"), TimiFXIcon.get(TimiIcon.FOLDER_ADD));
		MenuItem menuRename = new MenuItem(TimiFXUI.MULTILINGUAL.text("rename"));
		MenuItem menuRefresh = new MenuItem(TimiFXUI.MULTILINGUAL.text("refresh"), TimiFXIcon.get(TimiIcon.REFRESH));
		MenuItem menuDestroy = new MenuItem(TimiFXUI.MULTILINGUAL.text("delete"), TimiFXIcon.get(TimiIcon.FAIL, RED));

		setContextMenu(new ContextMenu(menuMkdir, menuRename, menuRefresh, TimiFXUI.sep(), menuDestroy));

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

		// 新建文件夹
		menuMkdir.disableProperty().bind(menuRefresh.disableProperty());
		menuMkdir.setOnAction(e -> mkdir(getSelectionModel().getSelectedItem()));

		// 重命名
		menuRename.disableProperty().bind(Observables.onlyOnceInList(getSelectionModel().getSelectedItems()).not());
		menuRename.setOnAction(e -> rename(getSelectionModel().getSelectedItem()));

		// 刷新
		menuRefresh.disableProperty().bind(Bindings.createBooleanBinding(() -> {
			List<TreeItem<File>> items = getSelectionModel().getSelectedItems();
			// 没有选择、多选、选的不是文件时禁用
			return items == null || items.size() != 1 || items.getFirst().getValue().isFile();
		}, getSelectionModel().selectedItemProperty()));
		menuRefresh.setOnAction(e -> refreshItem(getSelectionModel().getSelectedItem()));

		// 删除
		List<File> roots = List.of(File.listRoots());
		menuDestroy.disableProperty().bind(Bindings.createBooleanBinding(() -> {
			ObservableList<TreeItem<File>> items = getSelectionModel().getSelectedItems();
			if (items.isEmpty()) {
				return true;
			}
			for (TreeItem<File> item : items) {
				if (roots.contains(item.getValue())) {
					return true;
				}
			}
			return false;
		}, getSelectionModel().getSelectedItems()));
		menuDestroy.setOnAction(e -> destroy(getSelectionModel().getSelectedItems()));

		// 快捷键
		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) {
				switch (code) {
					case F2 -> menuRename.fire();
					case F5 -> menuRefresh.fire();
					case DELETE -> menuDestroy.fire();
				}
			}
			if (control && shift && !alt && code == KeyCode.N) {
				menuMkdir.fire();
			}
		});

		// ---------- 就绪 ----------

		// 过滤隐藏文件
		CallbackArgReturn<File, Boolean> filterHidden = file -> !file.isHidden();
		itemFilters.add(filterHidden);
		showHide.addListener((obs, o, n) -> {
			if (isShowHide()) {
				itemFilters.remove(filterHidden);
			} else {
				itemFilters.add(filterHidden);
			}
		});

		// 默认磁盘根目录
		for (File root : roots) {
			getRoots().add(new FileItem(root));
		}
	}

	/**
	 * 创建文件夹
	 *
	 * @param base 基于文件夹
	 */
	public void mkdir(TreeItem<File> base) {
		if (base == null) {
			return;
		}
		AlertTextField alert = new AlertTextField(TimiFXUI.MULTILINGUAL.text("file.mkdir"));
		alert.setTips(TimiFXUI.MULTILINGUAL.text("name"));
		alert.setOnActionEvent(action -> {
			if (action == AlertButton.Action.CONFIRM) {
				try {
					IO.dir(IO.fitPath(base.getValue().getAbsolutePath()) + alert.getText());
					refreshItem(getSelectionModel().getSelectedItem());
				} catch (NoPermissionException ex) {
					ex.printStackTrace();
				}
			}
			return true;
		});
		alert.autoSize().showRelativeCenter(getScene().getWindow());
	}

	/**
	 * 重命名
	 *
	 * @param file 文件或文件夹
	 */
	public void rename(TreeItem<File> file) {
		if (file == null) {
			return;
		}
		AlertTextField alert = new AlertTextField(TimiFXUI.MULTILINGUAL.text("file.rename"));
		alert.setTips(TimiFXUI.MULTILINGUAL.text("name"));
		alert.setOnActionEvent(action -> {
			if (action == AlertButton.Action.CONFIRM) {
				IO.rename(getSelectionModel().getSelectedItem().getValue(), alert.getText());
				refreshItem(getSelectionModel().getSelectedItem().getParent());
			}
			return true;
		});
		alert.autoSize().showRelativeCenter(getScene().getWindow());
	}

	/**
	 * 销毁文件
	 *
	 * @param files 文件节点列表
	 */
	public void destroy(List<TreeItem<File>> files) {
		if (TimiJava.isEmpty(files)) {
			return;
		}
		new AlertConfirm(TimiFXUI.MULTILINGUAL.text("file.destroy")) {

			@Override
			protected void onConfirm() {
				new RunAsync<TreeItem<File>>() {

					@Override
					protected TreeItem<File> call() {
						List<File> items = files.stream().map(TreeItem::getValue).toList();
						for (File file : items) {
							IO.destroy(file);
						}
						// 查找最高级节点刷新
						int l = Integer.MAX_VALUE;
						TreeItem<File> item = null;
						for (int i = 0, j; i < items.size(); i++) {
							j = getTreeItemLevel(files.get(i));
							if (j < l) {
								l = j;
								item = files.get(i);
								if (item.getParent() != null) {
									item = item.getParent();
								}
							}
						}
						return item;
					}

					@Override
					protected void onFinish(TreeItem<File> item) {
						if (item != null) {
							refreshItem(item);
						}
					}

					@Override
					protected void onException(Throwable e) {
						AlertTips.error(getScene().getWindow(), TimiFXUI.MULTILINGUAL.text("file.tips.destroy_fail"));
					}
				}.start();
			}
		}.autoSize().showRelativeCenter(getScene().getWindow());
	}

	/**
	 * 刷新子节点
	 *
	 * @param treeItem 父级节点
	 */
	public void refreshItem(TreeItem<File> treeItem) {
		if (treeItem instanceof FileItem fileItem) {
			fileItem.getChildren().clear();
			fileItem.getChildren().add(new FileItem());
			fileItem.asyncLoadChildren();
		}
	}

	/**
	 * 刷新子节点
	 *
	 * @param fileItem 父级节点
	 */
	public void refreshItem(FileItem fileItem) {
		fileItem.getChildren().clear();
		fileItem.getChildren().add(new FileItem());
		fileItem.asyncLoadChildren();
	}

	/**
	 * 选择目标路径
	 *
	 * @param path 路径
	 */
	public void selectItem(String path) {
		if (TimiJava.isEmpty(path)) {
			path = "./";
		}
		selectItem(new File(path));
	}

	/**
	 * 选择目标文件
	 *
	 * @param file 目标文件
	 */
	public void selectItem(File file) {
		File parent = file.getAbsoluteFile();
		if (!parent.exists()) {
			parent = new File("./").getAbsoluteFile();
		}
		do {
			selectDeque.addFirst(parent);
		} while ((parent = parent.getParentFile()) != null);

		if (TimiJava.isNotEmpty(selectDeque)) {
			ObservableList<TreeItem<File>> roots = getRoots();
			for (TreeItem<File> root : roots) {
				root.setExpanded(false);
				if (root.getValue().equals(selectDeque.getFirst())) {
					selectDeque.removeFirst();
					root.setExpanded(true);
					if (TimiJava.isEmpty(selectDeque)) {
						break;
					}
				}
			}
		}
	}

	/**
	 * 添加构建节点过滤器，返回 false 时不创建该节点
	 *
	 * @param itemFilter 节点过滤器
	 */
	public void addItemFilter(CallbackArgReturn<File, Boolean> itemFilter) {
		itemFilters.add(itemFilter);
	}

	/**
	 * 移除构建节点过滤器
	 *
	 * @param itemFilter 节点过滤器
	 */
	public void removeItemFilter(CallbackArgReturn<File, Boolean> itemFilter) {
		itemFilters.remove(itemFilter);
	}

	/**
	 * 设置是否显示隐藏文件
	 *
	 * @param showHide true 为显示隐藏文件
	 */
	public void setShowHide(boolean showHide) {
		this.showHide.set(showHide);
	}

	/**
	 * 当前是否显示隐藏文件
	 *
	 * @return true 为显示隐藏文件
	 */
	public boolean isShowHide() {
		return showHide.get();
	}

	/**
	 * 获取显示隐藏文件监听
	 *
	 * @return 显示隐藏文件监听
	 */
	public BooleanProperty showHideProperty() {
		return showHide;
	}

	/**
	 * 获取正在查找节点监听，此时属于被动展开，用于阻止主动展开的加载节点
	 *
	 * @return 正在查找节点监听
	 */
	public BooleanBinding findingItemProperty() {
		return findingItem;
	}

	/**
	 * 文件节点
	 *
	 * @author 夜雨
	 * @since 2023-03-16 00:23
	 */
	public final class FileItem extends TreeItem<File> {

		/** 默认构造，此时为占位节点 */
		FileItem() {
			this(null);
		}

		/**
		 * 标准构造
		 *
		 * @param file 文件
		 */
		FileItem(File file) {
			super(file);

			if (file != null && file.isDirectory()) {
				getChildren().add(new FileItem(null)); // 占位
			}

			// 展开
			expandedProperty().addListener((obs, o, isExpanded) -> {
				if (isExpanded) {
					getSelectionModel().clearSelection();
					getSelectionModel().select(this);
					asyncLoadChildren();
				} else {
					getChildren().add(new FileItem());
				}
			});
		}

		/** 异步加载子节点 */
		void asyncLoadChildren() {
			RunAsync.callbackReturn(() -> {
				List<FileItem> fileItems = new ArrayList<>();
				File[] files = getValue().listFiles();
				if (files != null) {
					// 排序
					List<File> fileList = Arrays.stream(files).sorted(OS.FileSystem.COMPARATOR_FILE_NAME).toList();

					// 过滤
					list:
					for (File file : fileList) {
						for (CallbackArgReturn<File, Boolean> itemFilter : itemFilters) {
							if (!itemFilter.handler(file)) {
								continue list;
							}
						}
						fileItems.add(new FileItem(file));
					}
				}
				return fileItems;
			}, items -> {
				getChildren().setAll(items);

				if (TimiJava.isNotEmpty(selectDeque)) {
					File file = selectDeque.removeFirst();
					for (FileItem item : items) {
						if (item.getValue().equals(file)) {
							if (item.getValue().isDirectory()) {
								item.setExpanded(true);
							}
							break;
						}
					}
					if (TimiJava.isEmpty(selectDeque)) {
						// 执行选中
						Platform.runLater(() -> scrollTo(getSelectionModel().getSelectedIndex() - 5));
					}
				}
			});
		}
	}
}
