import { IEnumeratorV2, IEnumeratorV2Started } from "../IEnumeratorV2";
import { CoroutineExecutor } from "./CoroutineExecutor";

let EnumeratorExecutorClass: typeof import("./EnumeratorExecutor").EnumeratorExecutor = null;
let SingleEnumeratorClass: typeof import("./SingleEnumerator").SingleEnumerator = null;
let ParallelEnumeratorClass: typeof import("./ParallelEnumerator").ParallelEnumerator = null;
let WaitTimeEnumeratorClass: typeof import("./WaitTimeEnumerator").WaitTimeEnumerator = null;
let ActionEnumeratorClass: typeof import("./ActionEnumerator").ActionEnumerator = null;

/**
 * 使用前初始場景第一個事件一定要先Init過
 * @example
 * 而且不能同時間有其他onLoad在跑,放start可以
 * @example
 * protected async onLoad(): Promise<void> {
 * 	await BaseEnumerator.Init();
 * }
 */
export abstract class BaseEnumerator implements IEnumeratorV2 {
	public nextEnumerator: BaseEnumerator;

	abstract next(value?: any): IteratorResult<any>;

	public static isInit: boolean = false;

	public static async Init(): Promise<any> {
		await Promise.all([
			(async () => {
				EnumeratorExecutorClass = (await import("./EnumeratorExecutor")).EnumeratorExecutor;
			})(),
			(async () => {
				SingleEnumeratorClass = (await import("./SingleEnumerator")).SingleEnumerator;
			})(),
			(async () => {
				ParallelEnumeratorClass = (await import("./ParallelEnumerator")).ParallelEnumerator;
			})(),
			(async () => {
				WaitTimeEnumeratorClass = (await import("./WaitTimeEnumerator")).WaitTimeEnumerator;
			})(),
			(async () => {
				ActionEnumeratorClass = (await import("./ActionEnumerator")).ActionEnumerator;
			})(),
		]);
		BaseEnumerator.isInit = true;
	}

	Start(target?: any): IEnumeratorV2Started {
		let executor = LazyLoad.EnumeratorExecutor(this, target);
		CoroutineExecutor.instance.StartCoroutine(executor);
		return executor;
	}

	Then(iterator: Iterator<any>): IEnumeratorV2 {
		if (!iterator) {
			return this;
		}

		if (iterator instanceof BaseEnumerator) {
			BaseEnumerator.getLastEnumerator(this).nextEnumerator = iterator;
			return this;
		} else {
			let enumerator = LazyLoad.SingleEnumerator(iterator);
			BaseEnumerator.getLastEnumerator(this).nextEnumerator = enumerator;
			return this;
		}
	}

	ThenSerial(...iterators: Iterator<any>[]): IEnumeratorV2 {
		let last = BaseEnumerator.getLastEnumerator(this);
		for (let iterator of iterators) {
			if (iterator instanceof BaseEnumerator) {
				last.nextEnumerator = iterator;
			} else {
				let enumerator = LazyLoad.SingleEnumerator(iterator);
				last.nextEnumerator = enumerator;
			}
			last = last.nextEnumerator;
		}
		return this;
	}

	ThenParallel(...iterators: Iterator<any>[]): IEnumeratorV2 {
		return this.Then(LazyLoad.ParallelEnumerator(...iterators));
	}

	ThenAction(action: Function, delaySeconds?: number): IEnumeratorV2 {
		if (delaySeconds > 0) {
			return this.ThenSerial(LazyLoad.WaitTimeEnumerator(delaySeconds), LazyLoad.ActionEnumerator(action));
		} else {
			return this.Then(LazyLoad.ActionEnumerator(action));
		}
	}

	ThenWaitTime(seconds: number): IEnumeratorV2 {
		return this.Then(LazyLoad.WaitTimeEnumerator(seconds));
	}

	static getLastEnumerator(enumerator: BaseEnumerator): BaseEnumerator {
		let next = enumerator;
		while (next.nextEnumerator) {
			next = next.nextEnumerator;
		}
		return next;
	}
}

module LazyLoad {
	export function EnumeratorExecutor(enumerator: BaseEnumerator, target: any) {
		let newclass = new EnumeratorExecutorClass(enumerator, target);
		return newclass;
		// return new (require("./EnumeratorExecutor") as typeof import("./EnumeratorExecutor")).EnumeratorExecutor(enumerator, target);
	}

	export function SingleEnumerator(iterator: Iterator<any>) {
		let newclass: any = new SingleEnumeratorClass(iterator);
		return newclass;
		// return new (require("./SingleEnumerator") as typeof import("./SingleEnumerator")).SingleEnumerator(iterator);
	}

	export function ParallelEnumerator(...iterators: Iterator<any>[]) {
		let newclass: any = new ParallelEnumeratorClass(iterators);
		return newclass;
		// return new (require("./ParallelEnumerator") as typeof import("./ParallelEnumerator")).ParallelEnumerator(iterators);
	}

	export function WaitTimeEnumerator(seconds: number) {
		let newclass: any = new WaitTimeEnumeratorClass(seconds);
		return newclass;
		// return new (require("./WaitTimeEnumerator") as typeof import("./WaitTimeEnumerator")).WaitTimeEnumerator(seconds);
	}

	export function ActionEnumerator(action: Function) {
		let newclass: any = new ActionEnumeratorClass(action);
		return newclass;
		// return new (require("./ActionEnumerator") as typeof import("./ActionEnumerator")).ActionEnumerator(action);
	}
}