import Manager from "./Manager";
import AppFacade from "../AppFacade";
import AssetMgr from "./AssetMgr";
import { ResConst } from "../ConstDefine/ResConst";
import LoopMgr from "./LoopMgr";
import MathUtil from "../Util/MathUtil";
import GameUtil from "../Util/GameUtil";
import { EventConst } from "../ConstDefine/EventConst";

const ValidID=-1

class Info{
    constructor(public res: string, public Id: number = ValidID, public loop: boolean = true, public volume: number = 1) { }
}

export default class SoundMgr extends Manager{

    static get TypeName(): string{return "SoundMgr"}

    
    private musicDic:Map<string,Info>=new Map()
    private effectDic:Map<string,Info>=new Map()
    private tmpMusic:Info
    private effectResDic:Map<string,cc.AudioClip>=new Map()

    async start(){
        this.initSoundDB()
        this.playStartMusic()
        console.log("SoundMgr")
        cc.systemEvent.on(EventConst.ShowGame,this.onShowGame,this)
        cc.systemEvent.on(EventConst.HideGame,this.onHideGame,this)
       
    }

    public release(){
        console.log("SoundMgr onRelease")
        this.stopAllMusic()
        this.stopTmpMusic()
        this.musicDic.clear()
        this.effectResDic.clear()
        this.tmpMusic=undefined
        cc.systemEvent.off(EventConst.ShowGame,this.onShowGame,this)
        cc.systemEvent.off(EventConst.HideGame,this.onHideGame,this)
       
    }

    private onHideGame(){
        cc.log("cocos隐藏游戏")
        // cc.audioEngine.pauseAll()
    }

    private onShowGame(){
        cc.log("cocos显示游戏")
        // cc.audioEngine.resumeAll()
    }

    private initSoundDB() {
    }

    private playStartMusic(){
    }

    public tryChangeMusic(isOpen:boolean){
        cc.log("改变声音状态",isOpen)
        if(isOpen){
            // this.IsMusicOpen&&this.resumAllMusic()
            cc.audioEngine.resumeAll()
        }else{
            cc.audioEngine.pauseAll()
        }
    }

    //播放URL的音乐
    public async playMusicByUrl(resName: string, volume: number = 1, loop: boolean = true) {
        let info: Info = this.musicDic.get(resName)
        if (!info) {
            info = new Info(resName)
            //不循环的没必要加如缓存
            loop && this.musicDic.set(resName, info)
            info.loop = loop
            info.volume = volume
        }
        let audio = await this.loadResByUrl(resName) as cc.AudioClip;
        if (audio) {
            info.Id = cc.audioEngine.play(audio, loop, volume)
            cc.log("播放音乐", resName)
        }
    }

    //播放场景中的音乐
    public async playResMusic(resName:string,volume:number=1,loop:boolean=true){
        let info:Info=this.musicDic.get(resName)
        if(!info){
            info=new Info(resName)
            //不循环的没必要加如缓存
            loop&&this.musicDic.set(resName,info)
            info.loop=loop
            info.volume=volume
        }
        let audio = this.loadResClone(resName);
        if (audio) {
            info.Id = cc.audioEngine.play(audio, loop, volume)
            cc.log("播放音乐", resName)
        }
    }

    /**
     * 播放音乐，可以同时播放多个音乐
     * @param resName 
     * @param volume 
     * @param loop 
     */
    public async playMusic(resName:string,volume:number=1,loop:boolean=true){
        let info:Info=this.musicDic.get(resName)
        if(!info){
            info=new Info(resName)
            //不循环的没必要加如缓存
            loop&&this.musicDic.set(resName,info)
            info.loop=loop
            info.volume=volume
        }
        let audio = await this.loadResAsync(ResConst.AudioABName, resName)
        if (audio) {
            info.Id = cc.audioEngine.play(audio, loop, volume)
            cc.log("播放音乐", resName)
        }
    }

    /**
     * 播放临时音乐，不会被记录下来musicDic
     * @param resName 
     * @param volume 
     * @param loop 
     * @param resume 播放结束自动切回背景音乐吗
     */
    public async playTmpMusic(resName:string,volume:number=1,loop:boolean=true,resume:boolean=false){        
        if(this.tmpMusic){
            this.stopTmpMusic()
        }
        this.tmpMusic=new Info(resName)
        let audio=await this.loadResAsync(ResConst.AudioABName,"Bgm/"+resName)
        let musicTime=0
        if(audio){
            this.tmpMusic.loop=loop
            this.tmpMusic.volume=volume
            this.tmpMusic.Id=cc.audioEngine.play(audio,loop,volume)
            musicTime=audio.duration
        }
        if(resume&&musicTime>0){
            AppFacade.getMgr(LoopMgr).setTimeout(()=>{
                this.resumAllMusic()
            },musicTime)
        }
    }

    public stopTmpMusic(){
        if(!this.tmpMusic) return
        if(this.tmpMusic.Id!=ValidID){
            cc.audioEngine.stop(this.tmpMusic.Id)
        }
        this.tmpMusic=null
    }

    public async resumAllMusic(){
        for(let v of this.musicDic){
            let info=v[1]
            await this.playMusic(info.res,info.volume,info.loop)
        }
    }

    public stopAllMusic(){
        cc.audioEngine.stopAll()
    }

    public stopMusic(resName:string){
        let info = this.musicDic.get(resName)
        this.musicDic.delete(resName)
        if(info&&info.Id!=ValidID){
            cc.audioEngine.stop(info.Id)
        }
    }


    //一套音效随机播放一个
    public playEffectR(resName:string[],volume:number=1){        
        let i=MathUtil.randInt(resName.length)
        this.playEffect(resName[i],0,volume)
    }

    public playEffect(resName:string,delay:number=0,volume:number=1,isLoop:boolean=false){
        if(GameUtil.isStringEmpty(resName)) return
        if (delay > 0) {
            AppFacade.getMgr(LoopMgr).setTimeout(this._playEffect.bind(this, resName, volume), delay)
        } else {
            this._playEffect(resName, volume, isLoop)
        }
    }

    public stopEffect(resName:string){
        if(GameUtil.isStringEmpty(resName)) return
        let info=this.effectDic.get(resName)
        if(info){
            cc.audioEngine.stopEffect(info.Id)
            this.effectDic.delete(resName)
        }
    }

    //播放URL的音效
    public async playEffectByUrl(resName: string, delay: number = 0, volume: number = 1, isLoop: boolean = false) {
        let audio = await this.loadResEffectByUrl(resName)
        if (audio) {
            let id = cc.audioEngine.play(audio, isLoop, volume)
            //长期循环播放才保存
            if (isLoop) {
                let info = new Info(resName, id, isLoop, volume)
                this.effectDic.set(resName, info)
            }
        }
    }

    //播放场景中的音效
    public playResEffect(resName:string,delay:number=0,volume:number=1,isLoop:boolean=false){
        let audio=this.loadResEffectClone(resName)
        if(audio){
            let id=cc.audioEngine.play(audio,isLoop,volume)
            //长期循环播放才保存
            if(isLoop){
                let info=new Info(resName,id,isLoop,volume)
                this.effectDic.set(resName,info)
            }
        }
    }
    
    private async _playEffect(resName:string,volume:number=1,isLoop:boolean=false){
        let audio=await this.loadEffectAsync(resName)
        if(audio){
            let id=cc.audioEngine.play(audio,isLoop,volume)
            //长期循环播放才保存
            if(isLoop){
                let info=new Info(resName,id,isLoop,volume)
                this.effectDic.set(resName,info)
            }
        }
    }

    private async loadResEffectByUrl(resName:string){
        let effect=this.effectResDic.get(resName)
        if(!effect){
            effect=await this.loadResByUrl(resName) as cc.AudioClip;
            effect&&this.effectResDic.set(resName,effect)
        }
        return effect
    }

    private loadResEffectClone(resName:string){
        let effect=this.effectResDic.get(resName)
        if(!effect){
            effect=this.loadResClone(resName)
            effect&&this.effectResDic.set(resName,effect)
        }
        return effect
    }

    private async loadEffectAsync(resName:string){
        let effect=this.effectResDic.get(resName)
        if(!effect){
            effect=await this.loadResAsync(ResConst.AudioABName,resName)
            effect&&this.effectResDic.set(resName,effect)
        }
        return effect
    }

    private async loadResAsync(abName:string,resName:string):Promise<cc.AudioClip>{
        let asMgr=AppFacade.getMgr(AssetMgr)
        return await asMgr.loadAssetAsync<cc.AudioClip>(abName,resName,cc.AudioClip)
    }

    private loadResClone(resName:string,instantiate:boolean=false){
        let prefab=cc.find(ResConst.AudioABName+"/"+resName);
        if (!prefab) {
            console.error("loadViewClone prefab is null")
            return
        }
        let clone = instantiate && cc.instantiate(prefab) || prefab
        return clone.getComponent(cc.AudioSource).clip;
    }

    private async loadResByUrl(resName:string){
        return new Promise((resolve,reject)=>{
            if(!resName){
                reject("文件名错误！");
            }
            cc.assetManager.loadRemote(resName, (err, audioClip) => {
                resolve(audioClip);
            });
        });
    }
}