package com.ecep.contract.controller;

import java.util.List;

import org.springframework.util.StringUtils;

import com.ecep.contract.model.BaseEnumEntity;
import com.ecep.contract.model.BasedEntity;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.model.NamedEntity;

import javafx.beans.property.Property;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;

public class ComboBoxUtils {

    static String formatEntity(Object t) {
        if (t == null) {
            return "全部";
        }
        if (t instanceof BasedEntity e) {
            return e.toPrettyString();
        }
        if (t instanceof BaseEnumEntity<?> e) {
            return e.getValue();
        }
        if (t instanceof NamedEntity named) {
            return named.getName();
        }
        return t.toString();
    }

    static <T> T fromString(List<T> dataset, String string) {
        if (dataset == null) {
            // 没办法识别
            return null;
        }
        if (!StringUtils.hasText(string)) {
            // 没办法识别
            return null;
        }
        for (T t : dataset) {
            if (t == null) {
                continue;
            }
            if (t instanceof BasedEntity e) {
                if (e.toPrettyString().equals(string)) {
                    return t;
                }
            }
            if (t instanceof BaseEnumEntity<?> e) {
                if (e.getValue().equals(string)) {
                    return t;
                }
            }
            if (t instanceof NamedEntity named) {
                if (named.getName().equals(string)) {
                    return t;
                }
            }
            if (t.toString().equals(string)) {
                return t;
            }
        }
        return null;
    }


    private static
    class ComboBoxStringConverter<T> extends javafx.util.StringConverter<T> {
        private final List<T> dataset;

        public ComboBoxStringConverter(ObservableList<T> list) {
            dataset = list;
        }

        @Override
        public String toString(T t) {
            return formatEntity(t);
        }

        @Override
        public T fromString(String string) {
            return ComboBoxUtils.fromString(dataset, string);
        }
    }

    public static <T> void initialComboBox(ComboBox<T> comboBox, List<T> items, boolean hasNull) {
        ObservableList<T> list = FXCollections.observableArrayList();
        if (hasNull) {
            list.add(null);
        }
        list.addAll(items);

        comboBox.setItems(list);
        comboBox.setConverter(new ComboBoxStringConverter<>(list));
    }

    static class EntityStringConverter<T> extends javafx.util.StringConverter<T> {
        private final List<T> dataset;

        EntityStringConverter(List<T> dataset) {
            this.dataset = dataset;
        }

        @Override
        public String toString(T t) {
            return formatEntity(t);
        }

        @Override
        public T fromString(String string) {
            return ComboBoxUtils.fromString(dataset, string);
        }
    }

    public static <T extends IdentityEntity & NamedEntity> void initialComboBox(
            ComboBox<T> comboBox, Property<T> property, List<T> dataSet, boolean hasNull
    ) {
        ObservableList<T> list = FXCollections.observableArrayList();
        if (hasNull) {
            list.add(null);
        }
        list.addAll(dataSet);

        comboBox.setItems(list);
        comboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            property.setValue(newValue);
        });
        EntityStringConverter<T> converter = new EntityStringConverter<>(dataSet);
        comboBox.setConverter(converter);
        comboBox.valueProperty().bindBidirectional(property);
    }

    public static <K> void bindComboBox(ComboBox<K> comboBox, Property<K> property) {
        property.addListener((observable, oldValue, newValue) -> {
            comboBox.setValue(newValue);
//            comboBox.getItems().stream().filter(k->k.equals(newValue)).findFirst().ifPresent(comboBox::setValue);
        });

        comboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue == null) {
                property.setValue(null);
                return;
            }
            property.setValue(newValue);
        });
    }

    public static <K extends Enum<?>, T extends BaseEnumEntity<K>> void bindComboBox(ComboBox<T> comboBox, Property<K> property, List<T> dataSet) {
        property.addListener((observable, oldValue, newValue) -> {
            dataSet.stream().filter(l -> l.getType() == newValue).findFirst().ifPresent(comboBox::setValue);
        });

        comboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue == null) {
                property.setValue(null);
                return;
            }
            property.setValue(newValue.getType());
        });

//        comboBox.valueProperty().addListener((observable, oldValue, newValue) -> {
//            if (newValue == null) {
//                property.setValue(null);
//                return;
//            }
//            property.setValue(newValue.getType());
//        });

        comboBox.setCellFactory(param -> new ListCell<>() {
            @Override
            protected void updateItem(T item, boolean empty) {
                super.updateItem(item, empty);
                if (empty || item == null) {
                    setText(null);
                } else {
                    setText(item.getValue());
                }
            }
        });

        K value = property.getValue();
        if (value != null) {
            dataSet.stream().filter(l -> l.getType() == value).findFirst().ifPresent(comboBox::setValue);
        }
    }
}
