$Ajax={}

$Ajax.map=new Map()
$Ajax.timeout=1000*15;//15秒,只能用于ws

$Ajax.filter=new Map();//ajax中间件

$Ajax.path='';//ajax公共路径


$Ajax.setPath=function(path)
{
    $Ajax.path=path
}


$Ajax.wsInit=function(data)
{
    if(data instanceof Object)
    { 
        try 
        {
            if(data.$unique)
            {
                if($Ajax.map.has(data.$unique))
                {
                    $Ajax.map.get(data.$unique)(data);
                    $Ajax.map.delete(data.$unique);
                }
            }
        } 
        catch (error) 
        {
            $M.info('[ajax-ws]',error)
        }
        finally
        {
            if(data.$unique) $Ajax.map.delete(data.$unique)
        }
    }
}

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

$Ajax.filterFun=function(el)
{
    for (const iterator of $Ajax.filter) 
    {
        if(typeof iterator[0] == 'string')
        {
            if(this.$path.indexOf(iterator[0])!=-1)
            {
                let result=iterator[1].call(this,this.$path,el);
                if(result!==undefined)return result;
            }
        }
        else if(iterator[0] instanceof RegExp)
        {
            if(iterator[0].test(this.$path))
            {
                let result=iterator[1].call(this,this.$path,el);
                if(result!==undefined)return result;
            }
        }
    }
}

$Ajax.ws=async function(request,type)
{
    var obj={};
    obj.$unique=$M.rand();
    obj.$timeEnd=Date.now()+$Ajax.timeout
    obj.method=type;
    obj.path=request.url;
    obj.data=request.param;
    obj.user=request.user;
    
    
    let pro = new Promise
    (
        (res,rej)=>
        {
           
            if(typeof $Ws !='undefined' && $Ws.stat)
            {
                var date=new Date()
                date=date.getSeconds();
                if($Ws.send(obj)===false)
                {
                    let bo=$Ajax.filterFun.call({type:'ws',$path:obj.path},false);
                    if(bo!=undefined)rej({request:bo,type:'filter'});
                    else rej('ws消息发送失败'); 

                    return
                }
                
                let resl=(data)=>
                {
                    let bo=$Ajax.filterFun.call({type:'ws',$path:obj.path},data);
                    if(bo!=undefined)rej({request:bo,type:'filter'});
                    else res(data);
                }

                $Ajax.map.set(obj.$unique,resl)
            }
            else
            {
                let bo=$Ajax.filterFun.call({type:'ws',$path:obj.path},false);
                if(bo!=undefined)rej({request:bo,type:'filter'});
                else rej('ws未连接'); 
            }
        }
    )

    return $M.race(pro,$Ajax.timeout,'ajax-ws').finally
    (
        ()=>
        {
            $Ajax.map.delete(obj.$unique)
        }
    )
}

$Ajax._ajax=function(http,str,spFun)
{
    if(spFun instanceof Function)
    {
        var ot,oloaded
        var _obj={};
        http.upload.onprogress = function(evt)
        {
            // event.total是需要传输的总字节，event.loaded是已经传输的字节。如果event.lengthComputable不为真，则event.total等于0
            if (evt.lengthComputable) {}
            var nt = new Date().getTime();//获取当前时间
            var pertime = (nt-ot)/1000; //计算出上次调用该方法时到现在的时间差，单位为s
            ot = new Date().getTime(); //重新赋值时间，用于下次计算
            var perload = evt.loaded - oloaded; //计算该分段上传的文件大小，单位b
            oloaded = evt.loaded;//重新赋值已上传文件大小，用以下次计算
            //上传速度计算
            var speed = perload/pertime;//单位b/s
            var bspeed = speed;
            var units = 'b/s';//单位名称
            if(speed/1024>1){
                speed = speed/1024;
                units = 'k/s';
            }
            if(speed/1024>1){
                speed = speed/1024;
                units = 'M/s';
            }
            speed = speed.toFixed(1);
            
            _obj.load=evt.loaded
            _obj.total=evt.total
            _obj.progress=Math.round(evt.loaded / evt.total * 100)
            _obj.speed=speed+units
            _obj.need=((evt.total-evt.loaded)/bspeed).toFixed(1)
            spFun(_obj)
        };
        http.upload.onloadstart = function()
        {
            ot = new Date().getTime();   //设置上传开始时间
            oloaded = 0;//设置上传开始时，以上传的文件大小为0
        };
    }
    let pro = new Promise
    (
        (res,rej)=>
        {
            http.onload = function()
            {
                var _type=http.getResponseHeader('Content-Type');
                if( _type?_type.match(/json/i):'')
                {
                    try 
                    {
                        let jr=JSON.parse(http.responseText)
                        let bo=$Ajax.filterFun.call(http,jr);
                        if(bo!=undefined)rej({request:bo,type:'filter'});
                        else res(jr);
                    } 
                    catch (error) 
                    {
                        rej(false);
                        $M.info('AJAX：处理错误',error)
                    }
                }
                else
                {
                    let bo=$Ajax.filterFun.call(http,http.responseText);
                    if(bo!=undefined)rej({request:bo,type:'filter'});
                    else res(http.responseText);
                     
                }
            }
            http.onerror = function()
            {
                $M.info('[ajax]','请求失败')
                let bo=$Ajax.filterFun.call(http,false);
                if(bo!=undefined)rej({request:bo,type:'filter'});
                else rej(false); 
            }
        }
    )
    http.send(str); 

    return pro;
}


$Ajax.post=async function(request)
{
    if(!(request instanceof Object))
    {
        return false;
    }
    if(request.ws && typeof $Ws =='object' && $Ws.stat)
    {
        return await $Ajax.ws(request,'post')
    }
    if(request.ws && (request.param instanceof Object) && !request.param.$unique)request.param.$unique=$M.rand();

    var http=new XMLHttpRequest();
    http.open('POST',$Ajax.path+request.url);
    http.$path=request.url;
    switch (request.type) 
    {
        case 'json':
            http.setRequestHeader('Content-type','application/json; charset=utf-8');
            str=(request.param instanceof Object)?JSON.stringify(request.param):'{}';
        break;
        case 'auto':
            http.setRequestHeader('Content-type','auto');
            str=request.param?request.param:'';
        break;
        default:
            http.setRequestHeader('Content-type','application/x-www-form-urlencoded; charset=utf-8');
            str=$M.unParam(request.param);
        break;
    }
    return await $Ajax._ajax(http,str,request.progress)
}

$Ajax.get=async function(request)
{
    if(!(request instanceof Object))
    {
        return;
    }

    if(request.ws && typeof $Ws =='object' && $Ws.stat)
    {
        return await $Ajax.ws(request,'get')
    }
    if(request.ws && (request.param instanceof Object) && !request.param.$unique)request.param.$unique=$M.rand();
    var str=$M.unParam(request.param);
    var _url=request.url+(str?('?'+str):"");


    if(request.cache || request.cacheCheck)
    {
        var _datafun=request.fun
        if(_data)
        {
            if(_datafun)
            {
                _datafun(data)
            }
            else
            {
                return new Promise
                (
                    (reslove)=>
                    {
                        reslove(data)
                    }
                )
            }
            return;
        }
        else if(!request.cacheCheck)
        {
            return new Promise
            (
                (reslove)=>
                {
                    reslove(data)
                }
            )
        }
    }

    
    var http=new XMLHttpRequest();
    http.open('GET',$Ajax.path+_url);
    http.$path=_url;
    return await $Ajax._ajax(http,'',request.progress);
}

//可以直接发送二进制，或者表单
$Ajax.file=async function(request)
{
    if(!(request instanceof Object))
    {
        return false;
    }
    var url=request.url
    var file=request.param
    var progress=request.progress

    var http=new XMLHttpRequest();
    http.open('POST',$Ajax.path+url);
    http.$path=url;
    http.setRequestHeader('Name',request.name);
    if(request.binary)
    {
        http.setRequestHeader('Content-type','binary');
    }
    return await $Ajax._ajax(http,file,progress);
}

//上传由$Data,获取的文件
$Ajax.upload=async function(url,val,dom,progress)
{
    let obj=[]
    //存在dom则需要判断type，不存在则不判断
    if(!dom || dom.type=='file')
    {
        if(Array.isArray(val))
        {
            for (const key in val) 
            {
                let input=document.createElement('input')
                input.type='file'
                input.val=val[key]
                let param=new FormData()
                param.set('file',val[key])
                let result=await $Ajax.file({url:url,param,progress})   
                obj.push(result)
            }
        }
        else
        {
            obj.push(await $Ajax.file({url:url,param:val,progress}))
        }
    }
    return obj
}