
import {Texture} from "@engine/renderer/webGl/base/texture";
import {ResourceLink} from "@engine/resources/resourceLink";
import {Game} from "../core/game";
import {Queue, TaskRef} from "./queue";
import {IURLRequest, UrlLoader} from "@engine/resources/urlLoader";
import {ITexture} from "@engine/renderer/texture";


export class ResourceLoader {

    public readonly q: Queue = new Queue();

    constructor(private game: Game) {
        this.game = game;
    }

    public loadImage(req: string|IURLRequest): ResourceLink<ITexture> {
        const loader:UrlLoader = this.createUrlLoader(req);
        const link: ResourceLink<ITexture> = new ResourceLink(loader.getUrl());
        if (this.game.getRenderer().getCachedTarget(link)) {
            link.setTarget(this.game.getRenderer().getCachedTarget(link));
            return link;
        }
        loader.onProgress = (n:number)=>this.q.progressTask(taskRef,n);
        loader.onLoad = (buffer:ArrayBuffer)=>{
            if (taskRef.id===10) console.log('onload',loader.getUrl());
            this.game.getRenderer().loadTextureInfo(
                buffer,link,() => this.q.resolveTask(taskRef)
            );
        };
        const taskRef:TaskRef = this.q.addTask(() => {
            loader.load();
        });
        return link;
    }

    public loadText(req: string|IURLRequest): ResourceLink<string> {
        const loader:UrlLoader = this.createUrlLoader(req);
        const link: ResourceLink<string> = new ResourceLink(loader.getUrl());
        this._loadText(loader, (data: string) => link.setTarget(data));
        loader.load();
        return link;
    }

    public loadJSON(req: string|IURLRequest): ResourceLink<string> {
        const loader:UrlLoader = this.createUrlLoader(req);
        const link: ResourceLink<string> = new ResourceLink(loader.getUrl());
        this._loadText(loader, (data: string) => link.setTarget(JSON.parse(data)));
        loader.load();
        return link;
    }

    public loadSound(req: string|IURLRequest): ResourceLink<void> {
        const loader:UrlLoader = this.createUrlLoader(req);
        const link: ResourceLink<void> = new ResourceLink(loader.getUrl());
        if (this.game.getAudioPlayer().isCached(link)) {
            return link;
        }
        const taskRef:TaskRef = this.q.addTask(() =>{
            loader.load();
        });
        loader.onProgress = (n:number)=>this.q.progressTask(taskRef,n);
        loader.onLoad = (data:ArrayBuffer)=>{
            this.game.getAudioPlayer().loadSound(
                data, link, () => this.q.resolveTask(taskRef),
            );
        };
        return link;
    }

    public loadBinary(req: string|IURLRequest): ResourceLink<ArrayBuffer> {
        const loader:UrlLoader = this.createUrlLoader(req);
        const link: ResourceLink<ArrayBuffer> = new ResourceLink<ArrayBuffer>(loader.getUrl());
        const taskRef:TaskRef = this.q.addTask(() => {
            loader.onLoad = (buff: ArrayBuffer|string)=>{
                link.setTarget(buff as ArrayBuffer);
                this.q.resolveTask(taskRef);
            };
            loader.load();
        });
        return link;
    }

    public addNextTask(task: () => void) {
        const taskRef:TaskRef =  this.q.addTask(() => {
            task();
            this.q.resolveTask(taskRef);
        });
    }

    public startLoading(): void {
        this.q.start();
    }

    public isCompleted(): boolean {
        return this.q.isCompleted();
    }

    public getProgress(): number {
        return this.q.calcProgress();
    }

    public onProgress(fn: () => void): void {
        this.q.onProgress = fn;
    }

    public onCompleted(fn: () => void): void {
        this.q.onResolved = fn;
    }


    private createUrlLoader(req: string|IURLRequest):UrlLoader{
        let iReq:IURLRequest;
        if ((req as string).substr!==undefined){
            iReq = {url:req as string,responseType:'text',method:'GET'};
        } else iReq = req as IURLRequest;
        return new UrlLoader(iReq);
    }

    private _loadText(loader:UrlLoader, callback: (data:string)=>void): void {
        const taskRef:TaskRef = this.q.addTask(() => {
            loader.onLoad = (data: ArrayBuffer|string)=>{
                callback(data as string);
                this.q.resolveTask(taskRef);
            };
            loader.onProgress = (n:number)=>this.q.progressTask(taskRef,n);
        });
    }


}
