
$Webrtc={}


$Webrtc.config={
    'iceServers': [{
    'urls': 'stun:stun.l.google.com:19302'
    }] 
};



//websocket通信
$Webrtc.urlIcd='/ws/swap/webrtcIcd' //发送信令
$Webrtc.urlDes='/ws/swap/webrtcDes' //发送描述

$Webrtc.urlIcdTag='/webrtcIcd/' //设置信令
$Webrtc.urlDesTag='/webrtcDes/' //设置描述

//webrtc通信
$Webrtc.urlFileSend='/fsSend' //文件发送
$Webrtc.urlFileRecive='/fsRecive' //文件接收
$Webrtc.urlFileSendNotFind='/fsSendNotFind' //文件以不存在
$Webrtc.urlFileSendNotFind='/fsSendNotFind' //文件以不存在
$Webrtc.urlFileOver='/fsOver' //文件传输完成
$Webrtc.urlFileSendClose='/fsSendClose' //文件传输发送方关闭
$Webrtc.urlFileReciveClose='/fsReciveClose' //文件接收方传输关闭

$Webrtc.urlFileTimeout=1000*15//传输文件15秒未收到任何二进制数据则为超时

$Webrtc.urlFileTypeLen=1//传输文件类型头占用头部多少位，暂时无用
$Webrtc.urlFileLen=2//传输文件索引头占用头部多少位
$Webrtc.urlFileRandLen=16//传输文件唯一id头占用头部多少位，暂时无用

$Webrtc.global={}
$Webrtc.global.call='/call'//拨打电话
$Webrtc.global.callDeny='/callDeny'//拒绝拨打电话
$Webrtc.global.callAllow='/callAllow'//同意拨打电话
$Webrtc.global.callOver='/callOver'//结束通话


//收到文件发送请求的回调,this 需要 function，不能箭头函数
$Webrtc.urlFileSendFun=function(path,param)
{
    this._reciveFile(param.data)
} 
//收到文件接收请求的回调,this 需要 function，不能箭头函数
$Webrtc.urlFileReciveFun=function(path,param)
{
    this._sendFile(param.data)
} 

//收到文件传送完成请求的回调,this 需要 function，不能箭头函数
$Webrtc.urlFileSendCloseFun=function(path,param)
{
    let back=this.reciveClose()
    console.log('收到文件',back)
    return back
} 


//收到文件传送完成请求的回调,this 需要 function，不能箭头函数
$Webrtc.urlFileReciveCloseFun=function(path,param)
{
    this.sendClose(true)
} 



$Webrtc.global={}
$Webrtc.global.fileTime=1000*60*60*24//发送文件缓存时间

$Webrtc.user={}

$Webrtc.map=new Map()

$Webrtc.wsInit=function(data)
{
    if(data instanceof Object)
    { 
        try 
        {
            let path=$Webrtc.map.get(data.path)
            if(path)
            {
                path(data.path,data)
            }
        } 
        catch (error) 
        {
            $M.info('[webrtc-ws]',error)
        }
    }
}

if (typeof $Ws =='object')$Ws.make.push($Webrtc.wsInit)

 
$Webrtc.init=function({user,reply,close,err,errSend,errRecive,
closeSend,closeRecive,open,openSend,openRecive,fileCloseFun,bufferFun,
callAllowFun,callDenyFun,callOverFun,streamFun
})
{
    if($Webrtc.user[user] && this.channel)
    {
        if(open)open(user)
        if(openSend)openSend(user)
        return $Webrtc.user[user]
    }
    $Webrtc.user[user]=this

    let localConnection = new RTCPeerConnection($Webrtc.config);
    let sendChannel = localConnection.createDataChannel("sendChannel");
    sendChannel.onopen = ()=>
    {
        this.sendChannelTag=true
        $M.info(`[${user}]webrtc发送通道打开`)
        if(open)open(user)
        if(openSend)openSend(user)
    };
    sendChannel.onclose = ()=>
    {
        $Webrtc.user[user]=undefined
        this.sendChannelTag=false
        $M.info(`[${user}]webrtc发送通道关闭`)
        if(close)close(user)
        if(closeSend)closeSend(user)
    };
    sendChannel.onerror = function(err)
    {
        $M.info(`[${user}]webrtc发送通道出错`,err)
        if(err)err(user)
        if(errSend)errSend(user)
    }; 

    this.map=new Map()
    this.fileCahceMap=new Map()
    this.open=open
    this.openSend=openSend
    this.openRecive=openRecive
    this.close=close
    this.closeSend=closeSend
    this.closeRecive=closeRecive
    this.reply=reply
    this.user=user
    this.sendChannel=sendChannel
    this.channel=localConnection
    this.fileCloseFun=fileCloseFun
    this.bufferFun=bufferFun
    this.callAllowFun=callAllowFun
    this.callDenyFun=callDenyFun
    this.callOverFun=callOverFun
    this.streamFun=streamFun
    this.file={}
    this.fileTagSend=false
    this.metia//存媒体流，用于语音视频通话
    this.handleCreateDescriptionError=(err)=>
    {
        $M.info(`[${user}]webrtc创建描述错误`,err)
    }
    this.handleAddCandidateError=(err)=>
    {
        $M.info(`[${user}]webrtc添加信令错误`,err)
    }

    localConnection.ondatachannel = (event)=>
    {
        this.receiveChannelCallback(event,user)
    };

    localConnection.onicecandidate = async e => 
    {
      if(e.candidate)
      {
        $Ajax.ws({url:$Webrtc.urlIcd,param:e.candidate,user})
      }
    }

    this.filters()

    $Webrtc.map.set
    (
        $Webrtc.urlIcdTag+user,
        (path,param)=>
        {
            this.setIcd(param.data)
        }
    )
    
    $Webrtc.map.set
    (
        $Webrtc.urlDesTag+user,
        (path,param)=>
        {
            this.setDes(param.data)
            if(reply)
            {
                localConnection.createAnswer()
                .then(answer => localConnection.setLocalDescription(answer))
                .then(async () => $Ajax.ws({url:$Webrtc.urlDes,param:localConnection.localDescription,user}))
                .catch(this.handleCreateDescriptionError) 
            }
        }
    )
    if(!reply)
    {
        localConnection.createOffer()
        .then(offer => localConnection.setLocalDescription(offer))
        .then
        (
            async () => $Ajax.ws({url:$Webrtc.urlDes,param:localConnection.localDescription,user})
        ).catch(this.handleCreateDescriptionError)
    }
    
}

$Webrtc.init.prototype.filters=function()
{

    this.map.set
    (
        $Webrtc.global.callAllow,async (path,param)=>
        {
            if(this.callAllowFun)this.callAllowFun(param)
            else this._callAllow(param)
        }
    )
    this.map.set
    (
        $Webrtc.global.callOver,async (path,param)=>
        {
            if(this.callOverFun)this.callOverFun()
            else this._callOver()
        }
    )

    this.map.set
    (
        $Webrtc.callDeny,(path,param)=>
        {
            if(this.callDenyFun)this.callDenyFun(param)
        }
    )

    this.map.set
    (
        $Webrtc.urlFileReciveClose,(path,param)=>
        {
            $Webrtc.urlFileReciveCloseFun.call(this,path,param)
        }
    )

    this.map.set
    (
        $Webrtc.urlFileSendClose,(path,param)=>
        {
            $Webrtc.urlFileSendCloseFun.call(this,path,param)
        }
    )

    this.map.set
    (
        $Webrtc.urlFileRecive,(path,param)=>
        {
            $Webrtc.urlFileReciveFun.call(this,path,param)
        }
    )

    this.map.set
    (
        $Webrtc.urlFileSend,(path,param)=>
        {
            $Webrtc.urlFileSendFun.call(this,path,param)
        }
    )
}

$Webrtc.init.prototype.filter=function(data)
{
    if(data instanceof Object)
    { 
        try 
        {
            let path=this.map.get(data.path)
            if(path)
            {
                path(data.path,data)
            }
        } 
        catch (error) 
        {
            $M.info('[webrtc-ws]',error)
        }
    }
}


$Webrtc.init.prototype.setIcd=function(data)
{
    if(!this.channel)
    {
        $M.info(`[${this.user}]webrtc创建失败`)
        return 
    }
    this.channel
    .addIceCandidate(data)
    .catch(this.handleAddCandidateError)
}

$Webrtc.init.prototype.setDes=function(data)
{   
    if(!this.channel)
    {
        $M.info(`[${this.user}]webrtc创建失败`)
        return 
    }
    return this.channel
    .setRemoteDescription(data)
    .catch(this.handleCreateDescriptionError)
}

$Webrtc.init.prototype.sendClose=function(tag)
{
    try 
    {
        if(this.fileTagSend.readyState==='LOADING')this.fileTagSend.abort()
    } 
    catch (error) 
    {
        $M.info(`[${this.user}]webrtc发送文件失败`,error)
    }
    finally
    {
        this.fileTagSend=false
        if(!tag)this.send({message:'发送端关闭'},$Webrtc.urlFileSendClose)
    }
}

$Webrtc.init.prototype.reciveClose=function()
{
    let tag
    try 
    {
        let arr=this.file.arr
        
        if(!arr)return {status:false}
        tag=true
        arr=arr.sort
        (
            (a,b)=>
            {
                return a.index-b.index
            }
        )

        let arrResult=[]
        for (let index = 0; index < arr.length; index++) 
        {
            let cur=arr[index]
            arrResult.push(cur.data)
        }
        arr=arrResult

        let type=this.file.type
        let status=(this.file.totalCur==this.file.size)
        if(!arr || !arr.length)arr=['']
        arr=new Blob(arr,{type})
        Reflect.deleteProperty(this.file,'arr')
        let blob=arr;
        let back={...this.file,blob,status}
        this.file={}
        return back
    } 
    catch (error) 
    {
        $M.info(`[${this.user}]webrtc发送接收关闭失败`,error)
    }
    finally
    {
        this.fileTagSend=false
        if(tag)this.send({message:'接收端关闭'},$Webrtc.urlFileReciveClose)
        if(this.file.timeout)clearTimeout(this.file.timeout)
    }
}

$Webrtc.init.prototype.send=function(message,path)
{
    if(!this.channel || !this.sendChannel || !this.sendChannelTag)
    {
        $M.info(`[${this.user}]webrtc创建失败/发送通道未打开`)
        return false
    }
    if(!message)
    {
        $M.info(`[${this.user}]webrtc发送失败，请输入内容`)
        return
    } 
    let back={}

    if(!(message instanceof Blob) && !(message instanceof ArrayBuffer))
    {
        back.path=path
        back.data=message
        back=JSON.stringify(back)
    }
    else
    {
        back=message
    }
    
    return this.sendChannel.send(back)
}

//input 的时候，需指定哪一个如file[0]
$Webrtc.init.prototype.sendFile=function(data,name)
{
    let rand=$M.rand()
    let param={rand,size:data.size,type:data.type,name:name?name:data.name}
    this.fileCahceMap.set(rand,data)
    setTimeout
    (
        ()=>this.fileCahceMap.delete(rand),$Webrtc.global.fileTime
    )
    return this.sendJson(param,$Webrtc.urlFileSend)
}


$Webrtc.init.prototype.call=function(fun)
{
    return this.sendJson({time:Date.now(),status:true},$Webrtc.global.call)
}

$Webrtc.init.prototype.callDeny=function(message)
{
    return this.sendJson({time:Date.now(),message},$Webrtc.global.callDeny)
}

$Webrtc.init.prototype.callAllow=function()
{
    this._callAddStream()
    return this.sendJson({time:Date.now()},$Webrtc.global.callAllow)
}

$Webrtc.init.prototype.callOver=function(data)
{
    $Metia.closeStream(this.media)
    return this.sendJson({time:Date.now()},$Webrtc.global.callOver)
}

$Webrtc.init.prototype._callAddStream=async function()
{
    let channel=this.channel
    if(!channel)
    {
        $M.info(`[${this.user}]webrtc/media连接不存在`)
        return 
    }

    let media
    try 
    {
        if(!this.metia)media=await new $Metia.init()
        if(media)
        {
            this.media=media
            channel.addstream(this.metia)
            //channel.addTrack()
        }   
    } 
    catch (error) 
    {
        $M.info(`[${this.user}]webrtc-media打开失败`,error)
    }
    finally
    {
        channel.onstream=(evt)=>
        {
            if(this.streamFun)this.streamFun(evt)
        }
    }
}

$Webrtc.init.prototype._callAllow=function()
{
    this._callAddStream()
}

$Webrtc.init.prototype._callOver=function(data)
{
    //this.channel.removeTrack()
    $Metia.closeStream(this.metia)
}


//input 的时候，需指定哪一个如file[0]
$Webrtc.init.prototype.sendNat=function(data)
{
    let reader=new FileReader()
    this.fileTagSend=reader
    reader.readAsArrayBuffer(data)
    reader.onload=()=>
    {
        let result=reader.result
        this.send(result)
    }
}

//input 的时候，需指定哪一个如file[0]
$Webrtc.init.prototype._sendFile=function(data)
{
    if(!data)
    {
        $M.info(`[${this.user}]webrtc私有文件发送，没有参数`)
        return 
    }
    let rand=data.rand
    data=this.fileCahceMap.get(rand)
    if(!data)
    {
        this.sendJson({message:'文件不存在'},$Webrtc.urlFileSendNotFind)
        $M.info('wertc文件不存在,可能以被销毁',data)
        return false
    }
    if(this.fileTagSend)
    {
        $M.info(`[${this.user}]webrtc文件正在发送请稍后`)
        return 
    }
    let int=0
    let reader=new FileReader()
    this.fileTagSend=reader
    reader.readAsArrayBuffer(data)
    reader.onload=()=>
    {
        let len=$Webrtc.urlFileLen
        let result=reader.result
        let r8=new Uint8Array(result)

        let buffer = new ArrayBuffer(result.byteLength+len,len);
        let b8=new Uint8Array(buffer)

        b8.set(r8,len)
        let dv=new DataView(buffer)
        dv.setUint16(0,int)
        int+=1
        this.send(buffer)
    }
    reader.onloadend=()=>
    {
        this.fileTagSend=false
        this.fileCahceMap.delete(rand)
        this.sendClose()
    }
    return reader
}

$Webrtc.init.prototype._reciveFile=function(data)
{
    if(!data)
    {
        $M.info(`[${this.user}]webrtc私有文件接收，没有参数`)
        return
    }
    this.file={...data,arr:[],totalCur:0}
    this.sendJson({rand:data.rand},$Webrtc.urlFileRecive)
}

$Webrtc.init.prototype._reciveOver=function(data)
{
    if(!data)
    {
        $M.info(`[${this.user}]webrtc私有文件接收，完成`)
        return
    }
    this.sendJson({rand:data.rand},$Webrtc.urlFileOver)
}

$Webrtc.init.prototype._reciveSendClose=function(data)
{
    if(!data)
    {
        $M.info(`[${this.user}]webrtc私有文件接收，关闭`)
        return
    }
    this.sendJson({rand:data.rand},$Webrtc.urlFileClose)
}

$Webrtc.init.prototype.sendJson=function(data,path)
{
    return this.send(data,path)
}
  

$Webrtc.init.prototype.receiveChannelCallback=function (event,user) 
{
    receiveChannel = event.channel;
    
    receiveChannel.onmessage = (event)=>
    {
        let data=event.data
        $M.info(`[${user}]webrtc收到消息`,data)
        if(!(event.data instanceof Blob) && !(data instanceof ArrayBuffer))
        {
            let back
            try 
            {
                back=JSON.parse(data)
            } 
            catch (error) 
            {
                back=data
            }
            this.filter(back)
        }
        else
        {
            if(this.file.size!==undefined)
            {
                let len=$Webrtc.urlFileLen
                let dv=new DataView(data)
                let index=dv.getUint16(0)
                data=data.slice(len)

                let timeout=this.file.timeout
                if(timeout)clearTimeout(timeout)
                this.file.totalCur+=data.byteLength
                
                if(this.file.totalCur>this.file.size)
                {
                    this.reciveClose()
                    return 
                }
                this.file.timeout=setTimeout
                (
                    ()=>
                    {
                        this.reciveClose()
                    },$Webrtc.urlFileTimeout
                )
                this.file.arr.push({index,data})
            }
            else
            {
                if(this.bufferFun)this.bufferFun(data)
            }
        }
    };
    receiveChannel.onopen = ()=>
    {
        $M.info(`[${user}]webrtc接收打开`)
        if(this.open)this.open(user)
        if(this.openRecive)this.openRecive(user)
    };
    receiveChannel.onclose = ()=>
    {
        $M.info(`[${user}]webrtc接收关闭`)
        if(this.close)this.close(user)
        if(this.closeRecive)this.closeRecive(user)
    };
    receiveChannel.onerror = (error)=>
    {
        $M.info(`[${user}]webrtc接收出现问题`,error)
        if(this.err)this.err(user)
        if(this.errRecive)this.errRecive(user)
    };
}
  