package cn.forevermc.launcher.view.components.control;

import cn.forevermc.launcher.util.Resources;
import cn.forevermc.launcher.util.Sound;
import javafx.beans.binding.Bindings;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Border;
import javafx.scene.layout.StackPane;
import lombok.Getter;
import com.imyeyu.fx.ui.TimiFXUI;
import com.imyeyu.fx.utils.BgFill;
import com.imyeyu.fx.utils.BgImage;
import com.imyeyu.fx.utils.BorderStroke;
import com.imyeyu.utils.Calc;

import java.util.ArrayList;
import java.util.List;

/**
 * MC 滑动选择
 *
 * @author 夜雨
 * @since 2022-11-23 15:31
 */
public class MCSlider extends StackPane implements TimiFXUI {

	private static final Border BORDER_WHITE = new BorderStroke(Colorful.WHITE).width(2).build();
	private static final Border BORDER_BLACK = new BorderStroke(Colorful.BLACK).width(2).build();

	/** 标签 */
	@Getter
	private final Label label;

	private final MCButton thumb;

	private final DoubleProperty min, max, step, value;
	private final ObjectProperty<Double[]> steps;

	public MCSlider() {
		min = new SimpleDoubleProperty();
		max = new SimpleDoubleProperty();
		step = new SimpleDoubleProperty();
		value = new SimpleDoubleProperty();
		steps = new SimpleObjectProperty<>();

		thumb = new MCButton() {{
			label.setVisible(false);
			label.setManaged(false);
		}};
		thumb.setPrefWidth(16);
		thumb.prefHeightProperty().bind(heightProperty());
		thumb.borderProperty().unbind();
		thumb.borderProperty().bind(Bindings.when(hoverProperty()).then(BORDER_WHITE).otherwise(BORDER_BLACK));
		thumb.setMouseTransparent(true);

		label = new Label();
		label.setPadding(new Insets(8));
		label.setTextFill(Colorful.WHITE);
		label.setEffect(Resources.TEXT_SHADOW);
		label.setMouseTransparent(true);

		setAlignment(thumb, Pos.CENTER_LEFT);
		setBackground(new BgFill(Colorful.BLACK).build());
		getChildren().addAll(new StackPane() {{
			setMargin(this, new Insets(2));
			setBackground(new BgImage(Resources.BTN).build());
			getChildren().add(new StackPane() {{
				setOpacity(.6);
				setBackground(BG.BLACK);
			}});
		}}, thumb, label);

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

		// 计算步进
		steps.bind(Bindings.createObjectBinding(() -> {
			if (step.get() <= 0) {
				return null;
			}
			List<Double> result = new ArrayList<>();
			// 去头尾所有步进值
			for (double i = min.get() + step.get(); i <= max.get() - step.get(); i += step.get()) {
				result.add(i);
			}
			return result.toArray(new Double[0]);
		}, min, max, step));
		min.set(0);
		max.set(100);
		step.set(-1);

		// 点下
		addEventFilter(MouseEvent.MOUSE_PRESSED, e -> {
			Sound.CLICK.play();
			calcValue(e);
			e.consume();
		});

		// 拖拽
		addEventFilter(MouseEvent.MOUSE_DRAGGED, e -> {
			calcValue(e);
			e.consume();
		});

		widthProperty().addListener((obs, o, n) -> calcNearValue(value.get()));
	}

	/**
	 * 计算滑块位置
	 *
	 * @param event 鼠标事件
	 */
	private void calcValue(MouseEvent event) {
		if (event.getX() < thumb.getWidth() * .5) {
			thumb.setTranslateX(0);
			value.set(min.get());
			return;
		}
		if (getWidth() - thumb.getWidth() * .5 < event.getX()) {
			thumb.setTranslateX(getWidth() - thumb.getWidth());
			value.set(max.get());
			return;
		}
		double percent = event.getX() / getWidth();
		if (step.get() == -1) {
			thumb.setTranslateX((int) getPrefWidth() * percent - thumb.getPrefWidth() * .5);
			value.set(max.get() * percent);
		} else {
			// 坐标对应值
			calcNearValue(event.getX() / getWidth() * max.get());
		}
	}

	/**
	 * 计算最近位置
	 *
	 * @param value 当前值
	 */
	private void calcNearValue(double value) {
		if (steps.get() == null) {
			thumb.setTranslateX((getPrefWidth() - thumb.getPrefWidth()) * (value / (max.get() - min.get())));
		} else {
			// 最近步进位置
			double min = steps.get()[0];
			double max = steps.get()[steps.get().length - 1];
			double nearIndex = (steps.get().length - 1) * ((value - this.min.get()) / (max - min)) - 1;
			// 最近值
			int index = Calc.round(nearIndex);
			if (0 <= index) {
				if (index < this.steps.get().length) {
					// 反向计算滑块位置
					this.value.set(steps.get()[index]);
					thumb.setTranslateX((int) (this.value.get() / this.max.get() * getWidth() - thumb.getWidth() * .5));
				} else {
					// 达到最大值
					this.value.set(this.max.get());
					thumb.setTranslateX((int) (getWidth() - thumb.getPrefWidth()));
				}
			}
		}
	}

	/** @return 最小值 */
	public double getMin() {
		return min.get();
	}

	/** @param min 最小值 */
	public void setMin(double min) {
		this.min.set(min);
	}

	/** @return 最小值监听 */
	public DoubleProperty minProperty() {
		return min;
	}

	/** @return 最大值 */
	public double getMax() {
		return max.get();
	}

	/** @param max 最大值 */
	public void setMax(double max) {
		this.max.set(max);
	}

	/** @return 最大值监听 */
	public DoubleProperty maxProperty() {
		return max;
	}

	/** @return 步进 */
	public double getStep() {
		return step.get();
	}

	/** @param step 步进 */
	public void setStep(double step) {
		this.step.set(step);
	}

	/** @return 步进监听 */
	public DoubleProperty stepProperty() {
		return step;
	}

	/** @return 当前值 */
	public double getValue() {
		return value.get();
	}

	/** @param value 当前值 */
	public void setValue(double value) {
		this.value.set(value);
	}

	/** @return 当前值监听 */
	public DoubleProperty valueProperty() {
		return value;
	}
}
