package com.imyeyu.fx.ui.components;

import com.imyeyu.fx.bean.Interpolates;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.binding.Bindings;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.collections.ListChangeListener;
import javafx.geometry.Bounds;
import javafx.scene.control.SkinBase;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.util.Duration;

/**
 * 单选按钮组皮肤
 *
 * @author 夜雨
 * @since 2026-06-26
 */
public class RadioButtonGroupSkin extends SkinBase<RadioButtonGroup> {

	/** 动画时长 */
	private static final Duration SLIDER_DURATION = Duration.millis(320);

	/** 按钮容器 */
	private final HBox box;

	/** 滑块 */
	private final Region slider;

	/** 滑块 x */
	private final DoubleProperty sliderX;

	/** 滑块 y */
	private final DoubleProperty sliderY;

	/** 滑块宽度 */
	private final DoubleProperty sliderWidth;

	/** 滑块高度 */
	private final DoubleProperty sliderHeight;

	/** 滑块动画 */
	private Timeline sliderTimeline;

	/**
	 * 构造按钮组皮肤
	 *
	 * @param control 按钮组控件
	 */
	public RadioButtonGroupSkin(RadioButtonGroup control) {
		super(control);

		sliderX = new SimpleDoubleProperty();
		sliderY = new SimpleDoubleProperty();
		sliderWidth = new SimpleDoubleProperty();
		sliderHeight = new SimpleDoubleProperty();
		sliderTimeline = new Timeline();

		box = new HBox();
		box.getStyleClass().add(RadioButtonGroup.STYLE_CLASS_BOX);

		slider = new Region();
		slider.getStyleClass().add(RadioButtonGroup.STYLE_CLASS_SLIDER);
		slider.setManaged(false);
		slider.setMouseTransparent(true);

		sliderX.addListener((obs, o, n) -> applySliderBounds());
		sliderY.addListener((obs, o, n) -> applySliderBounds());
		sliderWidth.addListener((obs, o, n) -> applySliderBounds());
		sliderHeight.addListener((obs, o, n) -> applySliderBounds());

		box.alignmentProperty().bind(control.alignmentProperty());
		box.spacingProperty().bind(Bindings.createDoubleBinding(
			() -> control.getMode() == RadioButtonGroup.Mode.BUTTON && control.getSpacing() < 1 ? -1 : control.getSpacing(),
			control.modeProperty(),
			control.spacingProperty()
		));
		getChildren().add(new StackPane() {{
			getStyleClass().add(RadioButtonGroup.STYLE_CLASS_BOX);
			getChildren().addAll(slider, box);
		}});

		rebuildButtons();
		initListeners(control);
		updateMode();
	}

	/**
	 * 初始化监听
	 *
	 * @param control 控件
	 */
	private void initListeners(RadioButtonGroup control) {
		control.getButtons().addListener((ListChangeListener<ToggleButton>) change -> rebuildButtons());
		control.selectedButtonProperty().addListener((obs, o, n) -> syncSlider());
		control.modeProperty().addListener((obs, o, n) -> {
			updateMode();
			syncSlider();
		});
		box.layoutBoundsProperty().addListener((obs, o, n) -> {
			syncSlider();
		});
	}

	/**
	 * 重建按钮结构
	 */
	private void rebuildButtons() {
		box.getChildren().setAll(getSkinnable().getButtons());
		syncButtonStyleClass();
		syncSlider();
	}

	/**
	 * 同步按钮样式
	 */
	private void syncButtonStyleClass() {
		int size = getSkinnable().getButtons().size();
		for (int i = 0; i < size; i++) {
			ToggleButton button = getSkinnable().getButtons().get(i);
			button.getStyleClass().removeAll(
				RadioButtonGroup.STYLE_CLASS_BUTTON,
				RadioButtonGroup.STYLE_CLASS_FIRST,
				RadioButtonGroup.STYLE_CLASS_MIDDLE,
				RadioButtonGroup.STYLE_CLASS_LAST,
				RadioButtonGroup.STYLE_CLASS_SINGLE
			);
			button.getStyleClass().add(RadioButtonGroup.STYLE_CLASS_BUTTON);
			button.selectedProperty().addListener((obs, o, n) -> button.setViewOrder(n ? -1 : 0));
			button.setViewOrder(button.isSelected() ? -1 : 0);
			button.layoutBoundsProperty().addListener((obs, o, n) -> syncSlider());
			button.boundsInParentProperty().addListener((obs, o, n) -> syncSlider());

			if (size == 1) {
				button.getStyleClass().add(RadioButtonGroup.STYLE_CLASS_SINGLE);
				continue;
			}
			if (i == 0) {
				button.getStyleClass().add(RadioButtonGroup.STYLE_CLASS_FIRST);
				continue;
			}
			if (i == size - 1) {
				button.getStyleClass().add(RadioButtonGroup.STYLE_CLASS_LAST);
				continue;
			}
			button.getStyleClass().add(RadioButtonGroup.STYLE_CLASS_MIDDLE);
		}
	}

	/**
	 * 应用滑块位置和尺寸
	 */
	private void applySliderBounds() {
		slider.resizeRelocate(sliderX.get(), sliderY.get(), sliderWidth.get(), sliderHeight.get());
	}

	/**
	 * 更新模式
	 */
	private void updateMode() {
		boolean sliderMode = getSkinnable().getMode() == RadioButtonGroup.Mode.SLIDER;
		slider.setVisible(sliderMode);
	}

	/**
	 * 同步滑块位置
	 */
	private void syncSlider() {
		if (getSkinnable().getMode() != RadioButtonGroup.Mode.SLIDER) {
			sliderTimeline.stop();
			sliderX.set(0);
			sliderY.set(0);
			sliderWidth.set(0);
			sliderHeight.set(0);
			return;
		}
		ToggleButton button = getSkinnable().getSelectedButton();
		if (button == null || !box.getChildren().contains(button)) {
			sliderTimeline.stop();
			sliderX.set(0);
			sliderY.set(0);
			sliderWidth.set(0);
			sliderHeight.set(0);
			return;
		}
		Bounds bounds = button.getBoundsInParent();
		double targetX = bounds.getMinX() + 2;
		double targetWidth = Math.max(0, bounds.getWidth() - 4);
		double targetHeight = Math.max(0, box.getHeight() - 4);
		double targetY = 2;

		if (sliderWidth.get() <= 0 || sliderHeight.get() <= 0) {
			sliderTimeline.stop();
			sliderX.set(targetX);
			sliderY.set(targetY);
			sliderWidth.set(targetWidth);
			sliderHeight.set(targetHeight);
			return;
		}

		sliderTimeline.stop();
		sliderTimeline = new Timeline(
			new KeyFrame(
				SLIDER_DURATION,
				new KeyValue(sliderX, targetX, Interpolates.EASE_OUT_EXPO.getValue()),
				new KeyValue(sliderY, targetY, Interpolates.EASE_OUT_EXPO.getValue()),
				new KeyValue(sliderWidth, targetWidth, Interpolates.EASE_OUT_EXPO.getValue()),
				new KeyValue(sliderHeight, targetHeight, Interpolates.EASE_OUT_EXPO.getValue())
			)
		);
		sliderTimeline.play();
	}
}


