package com.ecep.contract.util;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Function;

import org.controlsfx.control.textfield.AutoCompletionBinding;
import org.controlsfx.control.textfield.TextFields;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import com.ecep.contract.Desktop;
import com.ecep.contract.Message;
import com.ecep.contract.converter.EntityStringConverter;
import com.ecep.contract.task.Tasker;

import javafx.application.Platform;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.DialogPane;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Screen;
import javafx.util.StringConverter;

public class UITools {
    private static final Logger logger = LoggerFactory.getLogger(UITools.class);

    public static void showExceptionAndWait(String title, Throwable e) {
        String stackTrace = printStackTrace(e);
        Label label = new Label("异常堆栈信息如下:");
        TextArea textArea = new TextArea(stackTrace);
        textArea.setEditable(false);
        textArea.setWrapText(true);

        GridPane.setVgrow(textArea, Priority.ALWAYS);
        GridPane.setHgrow(textArea, Priority.ALWAYS);

        GridPane expContent = new GridPane();
        expContent.setMaxWidth(Double.MAX_VALUE);
        expContent.add(label, 0, 0);
        expContent.add(textArea, 0, 1);

        String message = e.getMessage();
        showAndWait(Alert.AlertType.ERROR, "程序异常", title, message, expContent);
    }

    /**
     * 显示 Alert 窗口
     *
     * @param alertType alert 类型
     * @param title     标题
     * @param header    头
     * @param message   消息内容
     * @param content   扩张节点
     */
    public static CompletableFuture<ButtonType> showAndWait(Alert.AlertType alertType, String title, String header,
            String message, Node content) {
        CompletableFuture<ButtonType> future = new CompletableFuture<>();
        Platform.runLater(() -> {
            Alert alert = new Alert(alertType);
            if (content != null) {
                alert.getDialogPane().setExpandableContent(content);
                alert.getDialogPane().setExpanded(true);
            }
            alert.setTitle(title);
            if (header != null)
                alert.setHeaderText(header);
            if (message != null)
                alert.setContentText(message);

            Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
            alert.getDialogPane().setMaxWidth(visualBounds.getWidth() * 0.9);
            alert.showAndWait();
            future.complete(alert.getResult());
        });
        return future;
    }

    public static CompletableFuture<ButtonType> showConfirmation(String title, String message) {
        return showAndWait(Alert.AlertType.CONFIRMATION, "请选择", title, message, null);
    }

    public static <T> Dialog<T> createDialog() {
        Dialog<T> dialog = new Dialog<>();
        VBox box = new VBox();
        Label headerLabel = new Label();
        headerLabel.setId("header");

        VBox.setMargin(headerLabel, new Insets(5, 0, 8, 0));

        ListView<T> listView = new ListView<>();
        listView.setMinHeight(50);
        listView.setId("list-view");
        ObservableList<T> listViewDataSet = FXCollections.observableArrayList();
        listView.setItems(listViewDataSet);
        VBox.setVgrow(listView, Priority.ALWAYS);

        box.getChildren().setAll(headerLabel, listView);

        dialog.getDialogPane().setContent(box);
        dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
        dialog.setResizable(true);
        dialog.setWidth(600);
        dialog.setHeight(500);

        return dialog;
    }

    /**
     * 显示一个对话框, 并等待用户关闭对话框
     * <p>
     * 对话框内嵌一个 ListView，可在 consumer 中向 ListView 添加内容
     *
     * @param title    对话框标题
     * @param header   对话框头
     * @param consumer 对话框内容生成器
     */
    public static void showDialogAndWait(String title, String header, Consumer<ObservableList<String>> consumer) {
        Dialog<String> dialog = createDialog();
        dialog.setTitle(title);

        DialogPane dialogPane = dialog.getDialogPane();
        Node content = dialogPane.getContent();

        Label headerLabel = (Label) content.lookup("#header");
        ListView<String> listView = (ListView<String>) content.lookup("#list-view");
        headerLabel.setText(header);
        CompletableFuture.runAsync(() -> {
            consumer.accept(listView.getItems());
        }).exceptionally(e -> {
            Platform.runLater(() -> {
                listView.getItems().add(e.getMessage());
            });
            if (logger.isErrorEnabled()) {
                logger.error(e.getMessage(), e);
            }
            return null;
        });
        dialog.showAndWait();
    }

    /**
     * 显示一个对话框, 并等待用户关闭对话框
     *
     * @param title 对话框标题
     * @param task  任务
     * @param init  初始化
     */
    public static void showTaskDialogAndWait(String title, javafx.concurrent.Task<?> task,
            Consumer<Consumer<Message>> init) {
        Dialog<Message> dialog = createDialog();
        dialog.getDialogPane().getScene().getStylesheets().add("/ui/dialog.css");
        dialog.setTitle(title);

        DialogPane dialogPane = dialog.getDialogPane();
        VBox box = (VBox) dialogPane.getContent();
        Label headerLabel = (Label) box.lookup("#header");
        ListView<Message> listView = (ListView<Message>) box.lookup("#list-view");
        listView.setCellFactory(param -> new Tasker.MessageListCell());

        headerLabel.textProperty().bind(task.titleProperty());

        java.util.function.Predicate<Message> consumer = message -> {
            if (Platform.isFxApplicationThread()) {
                listView.getItems().add(message);
            } else {
                Platform.runLater(() -> listView.getItems().add(message));
            }
            return false;
        };
        if (task instanceof Tasker<?> tasker) {
            tasker.setMessageHandler(consumer);
        } else {
            task.messageProperty().addListener((observable, oldValue, newValue) -> {
                consumer.test(Message.info(newValue));
            });
        }

        // 加一个进度条
        ProgressBar progressBar = new ProgressBar(0);
        progressBar.setPrefHeight(12);
        progressBar.setPrefWidth(200);
        progressBar.prefWidthProperty().bind(box.widthProperty().subtract(10));
        progressBar.setMinHeight(12);
        progressBar.setVisible(true);
        VBox.setMargin(progressBar, new Insets(6, 0, 6, 0));

        Platform.runLater(() -> {
            box.getChildren().add(progressBar);
        });

        // progressBar.disabledProperty().bind(task.runningProperty());
        progressBar.visibleProperty().bind(task.runningProperty());
        progressBar.progressProperty().bind(task.progressProperty());

        if (task instanceof Tasker<?> tasker) {
            // 提交任务
            Desktop.instance.getExecutorService().submit(tasker);
        }
        if (init != null) {
            init.accept(msg -> consumer.test(msg));
        }
        dialog.showAndWait();
        if (task.isRunning()) {
            task.cancel();
        }
    }

    private static String printStackTrace(Throwable e) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        e.printStackTrace(pw);
        return sw.toString();
    }

    public static void showAlertAndWait(String message) {
        Alert alert = new Alert(Alert.AlertType.INFORMATION);
        alert.setContentText(message);
        alert.showAndWait();
    }

    public static <T> AutoCompletionBinding<T> autoCompletion(
            TextField textField,
            SimpleObjectProperty<T> property,
            EntityStringConverter<T> converter) {
        return autoCompletion(textField, property, converter::suggest, converter);
    }

    /**
     * 给 TextField 增加一个 自动补全功能
     *
     * @param textField 要补全的文本框
     * @param property  绑定的属性
     * @param func      获取补全数据的方法
     * @param converter 对象转换器
     * @param <T>       对象类型
     * @return AutoCompletionBinding
     */
    public static <T> AutoCompletionBinding<T> autoCompletion(
            TextField textField,
            SimpleObjectProperty<T> property,
            Function<AutoCompletionBinding.ISuggestionRequest, List<T>> func,
            StringConverter<T> converter) {
        // 先赋值
        textField.textProperty().set(converter.toString(property.get()));
        // 监听 property 变化
        property.addListener((observable, oldValue, newValue) -> {
            textField.textProperty().set(converter.toString(newValue));
        });

        // 关联一个 自动完成
        AutoCompletionBinding<T> completionBinding = TextFields.bindAutoCompletion(textField, p -> {
            if (p.isCancelled()) {
                return null;
            }
            try {
                return func.apply(p);
            } catch (Exception e) {
                textField.setText(e.getMessage());
                throw new RuntimeException(e);
            }
        }, converter);

        // 设置自动补全选中值到 property
        completionBinding.setOnAutoCompleted(event -> {
            property.set(event.getCompletion());
        });

        // fixed 当输入框丢失焦点时，输入框内容为空时，设置property 为null
        textField.focusedProperty().addListener((observable, oldValue, newValue) -> {
            if (!newValue) {
                if (!StringUtils.hasText(textField.getText())) {
                    property.set(null);
                }
            }
        });

        return completionBinding;
    }

    /**
     * 确认对话框
     *
     * @param title
     * @param text
     * @return
     */
    public static boolean showConfirmDialog(String title, String text) {
        // TODO
        Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
        alert.setTitle(title);
        alert.setHeaderText(text);
        if (alert.showAndWait().get() == ButtonType.OK) {
            return true;
        }
        return false;
    }
}
