package cn.forevermc.launcher.ctrl.pane;

import cn.forevermc.launcher.bean.Config;
import cn.forevermc.launcher.bean.FileDownload;
import cn.forevermc.launcher.bean.Page;
import cn.forevermc.launcher.core.DownloadThread;
import cn.forevermc.launcher.ctrl.Main;
import cn.forevermc.launcher.service.PageService;
import cn.forevermc.launcher.view.pages.ResourceDownloadPane;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.concurrent.Worker;
import javafx.scene.input.MouseEvent;
import javafx.scene.media.MediaPlayer;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import com.imyeyu.inject.annotation.Controller;
import com.imyeyu.inject.annotation.Inject;
import com.imyeyu.inject.annotation.InvokeForInjected;
import com.imyeyu.inject.annotation.SuperInject;
import com.imyeyu.io.IOSize;
import com.imyeyu.io.IOSpeedService;
import com.imyeyu.java.bean.Callback;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;

/**
 * 资源下载
 *
 * @author 夜雨
 * @since 2022-11-28 19:30
 */
@Slf4j
@Controller
@SuperInject
public class ResourceDownload extends ResourceDownloadPane {

	@Inject
	private Config config;

	@Inject
	private Main main;

	@Inject
	private PageService pageService;

	@Inject
	private IOSpeedService.Item ioSpeedItem;

	@Inject
	private ObjectProperty<MediaPlayer> bgmPlayer;

	/** 完成事件（无论如何都要触发，否则锁死启动线程） */
	@Setter
	private Callback onFinallyEvent;

	/** 队列 */
	private final Queue queue;

	private long downloadSize;
	private DownloadThread[] threads;

	public ResourceDownload() {
		queue = new Queue();

		// 取消
		cancel.setOnAction(e -> {
			if (threads != null) {
				for (int i = 0; i < threads.length; i++) {
					threads[i].cancel();
				}
			}
			queue.clear();
			list.getItems().clear();
			pageService.to(Page.MENU);
		});

		// 背景音乐
		bgmToggle.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> {
			if (bgmPlayer.get() == null) {
				e.consume();
			}
		});
		bgmToggle.selectedProperty().addListener((obs, o, isSelected) -> {
			MediaPlayer player = bgmPlayer.get();
			if (player != null) {
				if (isSelected) {
					player.play();
				} else {
					player.pause();
				}
			}
		});
	}

	@InvokeForInjected
	private void injected() {
		ioSpeedItem.addBufferListener(d -> Platform.runLater(() -> speed.setText(IOSize.format(d, IOSize.Unit.MB) + "/s")));
	}

	@Override
	public void onShow() {
		bgmToggle.visibleProperty().bind(config.getMain().getBgmVolume().greaterThan(0));
	}

	/** 开始下载 */
	public void start() {
		if (queue.isNotEmpty()) {
			pb.setProgress(-1);

			// 下载线程
			int threadSize = config.getLauncher().getMultiDownload().get();
			threads = new DownloadThread[Math.min(threadSize, queue.size())];
			for (int i = 0; i < threads.length; i++) {
				threads[i] = new DownloadThread(queue::safePull);
				threads[i].setOnFinallyEvent(() -> {
					for (int j = 0; j < threads.length; j++) {
						if (threads[j].getState() != Worker.State.SUCCEEDED) {
							return;
						}
					}
					// 所有任务结束
					onFinallyEvent.handler();
				});
				threads[i].setOnFinishEvent(file -> {
					// 完成
					file.progressProperty().unbind();
					list.getItems().remove(file);
					pb.setProgress(1 - 1D * list.getItems().size() / downloadSize);
				});
				threads[i].setOnErrorEvent(file -> {
					file.progressProperty().unbind();
					list.getItems().remove(file);
				});
			}
			for (int i = 0; i < threads.length; i++) {
				threads[i].start();
			}
		}
	}

	/**
	 * 设置下载队列，此方法会重置总进度
	 *
	 * @param list 列表
	 */
	public void setDownloadDeque(List<FileDownload> list) {
		downloadSize = list.size();
		this.list.getItems().addAll(list);
		queue.putDownloadDeque(list);
	}

	/**
	 * 追加下载队列，此方法不会重置总进度
	 *
	 * @param list 列表
	 */
	public void putDownloadDeque(List<FileDownload> list) {
		this.list.getItems().addAll(this.list.getItems().size(), list);
		queue.putDownloadDeque(list);
	}

	/**
	 * 下载队列
	 *
	 * @author 夜雨
	 * @since 2022-11-29 00:57
	 */
	private static class Queue {

		private final Deque<FileDownload> deque;

		public Queue() {
			deque = new ArrayDeque<>();
		}

		/**
		 * 追加队列
		 *
		 * @param list 列表
		 */
		void putDownloadDeque(List<FileDownload> list) {
			deque.addAll(list);
		}

		/** @return true 为队列已空 */
		boolean isEmpty() {
			return deque.isEmpty();
		}

		/** @return true 为队列非空 */
		boolean isNotEmpty() {
			return !isEmpty();
		}

		/** @return 队列大小 */
		int size() {
			return deque.size();
		}

		/** 清除队列 */
		void clear() {
			deque.clear();
		}

		/**
		 * 同步获取队列文件，可多线程操作
		 *
		 * @return 下载文件
		 */
		synchronized FileDownload safePull() {
			return deque.pollFirst();
		}
	}
}
