import { _decorator, Component, Node, AudioSourceComponent, loader, AudioClip, IsPowerOf2, url, instantiate, TERRAIN_HEIGHT_BASE } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('SoundManager')
export class SoundManager extends Component {


    /**
     * 背景音乐通道
     */
    private musicSoundChannel:AudioChannel;
    /**
     * 固定通道
     */
    private soundChannelMap:Map<string,AudioChannel>;
    /**
     * 重复利用通道
     */
    private recycleSoundChannelList:AudioChannel[];
    private recycleSoundChannelPool:AudioChannel[];

    onLoad():void{
        SoundManager.instance=this;
        this.soundChannelMap=new Map<string,AudioChannel>();
        this.recycleSoundChannelList=[];
        this.recycleSoundChannelPool=[];
    }

    start () {
        //背景音乐通道
        this.musicSoundChannel=new AudioChannel();
        this.musicSoundChannel.aSComponent=this.addComponent(AudioSourceComponent);
        //最大重复利用通道4个
        let channel:AudioChannel;
        for (let index = 0; index < 6; index++) {
            channel=new AudioChannel();
            channel.aSComponent=this.addComponent(AudioSourceComponent);
            this.recycleSoundChannelPool.push(channel);
        }
    }

    public PlayMusic(soundPath:string):void{
        this.musicSoundChannel.Play(soundPath,true);
    }

    /**
     * 固定通道播放音效
     * @param channel       通道
     * @param soundPath     音效地址
     * @param isLoop        是否循环播放
     * @param force         是否在相同音频的情况下强制从头开始播放
     */
    public PlaySound(channel:string,soundPath:string,isLoop:boolean=false,force:boolean=false):void{
        let audioChannel:AudioChannel;
        if(this.soundChannelMap.has(channel)==false){
            audioChannel=new AudioChannel();
            audioChannel.aSComponent=this.addComponent(AudioSourceComponent);
            this.soundChannelMap.set(channel,audioChannel);
        }else{
            audioChannel=this.soundChannelMap.get(channel);
        }
        audioChannel.Play(soundPath,isLoop,force);
    }

    /**
     * 清理通道
     * @param channel
     */
    public ClearSound(channel:string):void{
        if(this.soundChannelMap.has(channel)==false){
            return;
        }
        let audioChannel:AudioChannel=this.soundChannelMap.get(channel);
        audioChannel.Dispose();
        this.soundChannelMap.delete(channel);
    }

    /**
     * 停止某个固定通道的音效
     * @param channel 
     */
    public StopSound(channel:string):void{
        if(this.soundChannelMap.has(channel)==false){
            return;
        }
        let audioChannel:AudioChannel=this.soundChannelMap.get(channel);
        audioChannel.Stop();
    }

    /**
     * 某个固定频道是否在播放中
     * @param channel 
     */
    public ChannelIsPlaying(channel:string):boolean{
        if(this.soundChannelMap.has(channel)==false){
            return false;
        }
        let audioChannel:AudioChannel=this.soundChannelMap.get(channel);
        return audioChannel.isPlaying;
    }

    /**
     * 通道播放音效
     * @param soundPath     音效地址
     */
    public PlaySoundRecycleChannel(soundPath:string):void{
        let audioChannel:AudioChannel;
        //检测是否有已经播放完成的通道
        for (let index = 0; index < this.recycleSoundChannelList.length; index++) {
            const element = this.recycleSoundChannelList[index];
            if(element.isPlaying==false){
                this.recycleSoundChannelPool.push(element);
                this.recycleSoundChannelList.splice(index,1);
            }
        }
        //如果池中没有，则不播放该音效！
        if(this.recycleSoundChannelPool.length==0){
            return;
        }
        audioChannel=this.recycleSoundChannelPool.pop();
        this.recycleSoundChannelList.push(audioChannel);
        //标记为被占有
        audioChannel.Play(soundPath,false);
    }


    // update (deltaTime: number) {
    //     // Your update function goes here.
    // }


    private static instance:SoundManager;
    public static get single():SoundManager{
        return this.instance;
    }
}


class AudioChannel{
    /**
     * 音频资源
     */
    private static audioClipMap:Map<string,AudioClip>=new Map<string,AudioClip>();

    aSComponent:AudioSourceComponent;

    private soundUrl:string;
    private isLoop:boolean;
    private force:boolean;
    /**
     * 0 空闲 1加载中 2播放中
     */
    private state:number=0;
    constructor(){

    }

    Play(soundUrl:string,isLoop:boolean,force:boolean=false):void{
        if(this.soundUrl==soundUrl&&this.state==1){
            return;
        }
        this.soundUrl=soundUrl;
        this.isLoop=isLoop;
        this.force=force;
        this.state=1;
        let key:string=url.raw("resources/"+this.soundUrl);
        if(AudioChannel.audioClipMap.has(key)){
            let clip:AudioClip=AudioChannel.audioClipMap.get(key);
            console.log("播放音频缓存！");
            this.__play(clip);
        }else{
            loader.loadRes(this.soundUrl,AudioClip,this.audioLoadComplete.bind(this));
        }
    }

    private audioLoadComplete(err:Error,asset:AudioClip):void{
        if(err){
            console.error("加载音乐错误!");
            return;
        }
        let key:string=asset.nativeUrl.replace(asset["_native"],"");
        AudioChannel.audioClipMap.set(key,asset);
        if(this.state==0){
            return;
        }
        this.__play(asset);
    }

    private __play(audioClip:AudioClip):void{        
        this.state=2;
        if(this.aSComponent==null){
            return;
        }
        if(this.aSComponent.clip!=audioClip){
            this.aSComponent.stop();
            this.aSComponent.clip=audioClip;
        }
        this.aSComponent.loop=this.isLoop;
        if(this.force){
            this.aSComponent.currentTime=0;
        }
        this.aSComponent.play();
    }

    Stop():void{
        this.state=0;
        this.soundUrl=null;
        this.aSComponent.stop();
    }

    Dispose():void{
        this.soundUrl=null;
        this.aSComponent.destroy();
        this.aSComponent=null;
    }

    get isPlaying():boolean{
        return this.state>0;
    }
}