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


let EnumeratorExecutor_1: typeof import("./EnumeratorExecutor");

export abstract class BaseEnumerator implements IEnumeratorV2 {
    public nextEnumerator: BaseEnumerator;

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

    /**
     *
     */
    constructor() {
        this.onLoad();
    }

    async onLoad() {
        EnumeratorExecutor_1 = await import(`./EnumeratorExecutor`);
        return;
    }

    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 {
    let EnumeratorExecutor_Class: typeof import("./EnumeratorExecutor");
    let SingleEnumerator_Class: typeof import("./SingleEnumerator");
    let ParallelEnumerator_Class: typeof import("./ParallelEnumerator");
    let WaitTimeEnumerator_Class: typeof import("./WaitTimeEnumerator");
    let ActionEnumerator_Class: typeof import("./ActionEnumerator");
    export async function Init(): Promise<any> {
        EnumeratorExecutor_Class = await import(`./EnumeratorExecutor`);
        SingleEnumerator_Class = await import(`./SingleEnumerator`);
        ParallelEnumerator_Class = await import(`./ParallelEnumerator`);
        WaitTimeEnumerator_Class = await import(`./WaitTimeEnumerator`);
        ActionEnumerator_Class = await import(`./ActionEnumerator`);
        return null;
    }
    export function EnumeratorExecutor(enumerator: BaseEnumerator, target: any) {
        // return new (require("./EnumeratorExecutor") as typeof import("./EnumeratorExecutor")).EnumeratorExecutor(enumerator, target);
        return new (EnumeratorExecutor_Class).EnumeratorExecutor(enumerator, target);
    }

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

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

    export function WaitTimeEnumerator(seconds: number) {
        // return new (require("./WaitTimeEnumerator") as typeof import("./WaitTimeEnumerator")).WaitTimeEnumerator(seconds);
        return new (WaitTimeEnumerator_Class).WaitTimeEnumerator(seconds);
    }

    export function ActionEnumerator(action: Function) {
        // return new (require("./ActionEnumerator") as typeof import("./ActionEnumerator")).ActionEnumerator(action);
        return new (ActionEnumerator_Class).ActionEnumerator(action);
    }
}
export default LazyLoad;

// export class LazyLoadInit {
//     //#region public

//     public static async Init(): Promise<any> {
//         LazyLoad.EnumeratorExecutor_2 = await import(`./EnumeratorExecutor`);
//         return null;
//     }

//     //#endregion
// }

// export default LazyLoadInit;
