package com.imyeyu.fx.ui.components;

import com.imyeyu.fx.TimiFX;
import com.imyeyu.fx.icon.TimiFXIcon;
import com.imyeyu.fx.icon.TimiIcon;
import com.imyeyu.fx.ui.TimiFXUI;
import com.imyeyu.fx.utils.Nodes;
import com.imyeyu.utils.Time;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Popup;

import java.time.LocalDateTime;

/**
 * 时间选择器
 *
 * @author 夜雨
 * @since 2022-11-09 14:25
 */
public class TimePicker extends HBox implements TimiFXUI {

	private static final String STYLE_CLASS = "time-picker";
	private static final String STYLE_CLASS_POPUP = "time-picker-popup";
	private static final String STYLE_CLASS_TIME_BOX = "time-picker-time-box";
	private static final String STYLE_CLASS_NOW = "time-picker-now";

	private final Popup popup;
	private final TextField textField;
	private final ListView<String> hour, minute, second;

	/** 值 */
	private final IntegerProperty value;

	/** 默认构造器 */
	public TimePicker() {
		getStylesheets().add(TimePicker.class.getResource(RESOURCE + "style/components/time-picker.css").toExternalForm());

		value = new SimpleIntegerProperty(-1);

		textField = new TextField();
		textField.setEditable(false);
		textField.setAlignment(Pos.CENTER);
		textField.setPrefWidth(70);
		textField.setOnContextMenuRequested(Event::consume);
		IconButton button = new IconButton(TimiFXIcon.get(TimiIcon.CLOCK)).withBackground();
		button.setBorder(Stroke.EX_LEFT);

		// 时间选择
		hour = new ListView<>();
		minute = new ListView<>();
		second = new ListView<>();

		hour.setBorder(Stroke.RB);
		minute.setBorder(Stroke.RB);
		second.setBorder(Stroke.BOTTOM);

		// 此刻
		Button now = new Button(TimiFXUI.MULTILINGUAL.text("now"));
		now.setBorder(Stroke.LEFT);
		now.getStyleClass().add(STYLE_CLASS_NOW);

		HBox timeBox = new HBox(hour, minute, second);
		timeBox.getStyleClass().add(STYLE_CLASS_TIME_BOX);
		BorderPane root = new BorderPane();
		root.getStylesheets().add(TimePicker.class.getResource(RESOURCE + "style/components/time-picker.css").toExternalForm());
		root.getStyleClass().add(STYLE_CLASS_POPUP);
		BorderPane.setAlignment(now, Pos.CENTER_RIGHT);
		root.setPrefSize(140, 211);
		root.setCenter(timeBox);
		root.setBottom(now);

		popup = new Popup();
		popup.getContent().setAll(root);
		button.setOnAction(e -> {
			hour.scrollTo(hour.getSelectionModel().getSelectedIndex() - 3);
			minute.scrollTo(minute.getSelectionModel().getSelectedIndex() - 3);
			second.scrollTo(second.getSelectionModel().getSelectedIndex() - 3);

			Bounds bounds = textField.localToScreen(textField.getLayoutBounds());
			popup.setAutoHide(true);
			popup.show(textField, bounds.getMinX() - 5, bounds.getMaxY() - 6);
		});

		getStyleClass().add(STYLE_CLASS);
		setAlignment(Pos.CENTER_LEFT);
		getChildren().addAll(textField, button);

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

		for (int i = 0; i < 24; i++) {
			hour.getItems().add(String.format("%02d", i));
		}
		for (int i = 0; i < 60; i++) {
			minute.getItems().add(String.format("%02d", i));
		}
		second.getItems().addAll(minute.getItems());

		// 时间滚动居中
		hour.getSelectionModel().select(0);
		minute.getSelectionModel().select(0);
		second.getSelectionModel().select(0);

		EventHandler<ScrollEvent> middleScroll = e -> {
			if (e.getSource() instanceof ListView<?> list) {
				if (e.getDeltaY() < 0) {
					if (list.getSelectionModel().getSelectedIndex() < list.getItems().size() - 1) {
						list.getSelectionModel().selectNext();
					}
				} else {
					if (0 < list.getSelectionModel().getSelectedIndex()) {
						list.getSelectionModel().selectPrevious();
					}
				}
				list.scrollTo(list.getSelectionModel().getSelectedIndex() - 3);

				int h = hour.getSelectionModel().getSelectedIndex() * Time.HI;
				int m = minute.getSelectionModel().getSelectedIndex() * Time.MI;
				int s = second.getSelectionModel().getSelectedIndex() * Time.SI;

				setValue(h + m + s);
				e.consume();
			}
		};
		hour.addEventFilter(ScrollEvent.SCROLL, middleScroll);
		minute.addEventFilter(ScrollEvent.SCROLL, middleScroll);
		second.addEventFilter(ScrollEvent.SCROLL, middleScroll);

		Nodes.requestFocusOnHover(hour);
		Nodes.requestFocusOnHover(minute);
		Nodes.requestFocusOnHover(second);

		// 值监听
		value.addListener((obs, o, n) -> {
			if (value.get() < 0 || Time.D < value.get()) {
				throw new IllegalArgumentException("value must in [0, 86400000]");
			}

			LocalDateTime ldt = Time.toLocalDateTime(Time.today() + value.get());
			textField.setText("%02d:%02d:%02d".formatted(ldt.getHour(), ldt.getMinute(), ldt.getSecond()));

			// 选择器选中
			int s = value.get() / 1000;
			int h = s / 60 / 60;

			hour.getSelectionModel().select(h);
			minute.getSelectionModel().select(s / 60 - h * 60);
			second.getSelectionModel().select(s % 60);
		});

		// 此刻
		now.setOnAction(e -> {
			value.set((int) (Time.now() - Time.today()));

			LocalDateTime ldt = Time.toLocalDateTime(Time.now());
			hour.getSelectionModel().select(ldt.getHour());
			minute.getSelectionModel().select(ldt.getMinute());
			second.getSelectionModel().select(ldt.getSecond());

			hour.scrollTo(hour.getSelectionModel().getSelectedIndex() - 3);
			minute.scrollTo(minute.getSelectionModel().getSelectedIndex() - 3);
			second.scrollTo(second.getSelectionModel().getSelectedIndex() - 3);
		});

		popup.setOnShown(e -> {
			hour.scrollTo(hour.getSelectionModel().getSelectedIndex() - 3);
			minute.scrollTo(minute.getSelectionModel().getSelectedIndex() - 3);
			second.scrollTo(second.getSelectionModel().getSelectedIndex() - 3);
		});

		setValue(0);
	}

	/**
	 * 获取当前值，距离今天零时的时间戳
	 *
	 * @return 当前值
	 */
	public int getValue() {
		return value.get();
	}

	/**
	 * 获取值监听
	 *
	 * @return 值监听
	 */
	public IntegerProperty valueProperty() {
		return value;
	}

	/**
	 * 设置当前值，取值范围为一天时间戳 [0, 86400000]
	 *
	 * @param value 当前值
	 */
	public void setValue(int value) {
		this.value.set(value);
	}
}
