$Db={}
$_Db={};

$Db.initFun=[];

//index格式
//index可以直接为索引名，也可以为{name:'索引名',value:'索引值，可以为数组(框架解析为...arr)',type:'索引操作类型可不传',order:'排序'}

$_Db.fun=function(exe,tableName,index,fun)
{
    if(!fun && (index instanceof Function))
    {
        fun=index;
        index=undefined;
    }
    try   
    {
        if($_Db.stat=='success')
        {
            var transaction=$_Db.dbBase.transaction(tableName,'readwrite');
            var objectStore=transaction.objectStore(tableName);//获取表对象
            if(index)objectStore=objectStore.index(index.name?index.name:index);
            var result= exe(objectStore);
            if(!result && fun)
            {
                fun(false,false);
            }
            back(result,fun);
            return result;
        }
        else
        {
            $M.info('【db】数据库初始化中，请稍后操作');
        }
    } 
    catch (error) 
    {
        $M.info('【db】执行错误',error);
        back(false,fun);
        return false;
    }

    function back(result,fun)
    {
        if(fun && result)
        {
            result.onsuccess=function()
            {
                fun(this.result,true)
            }
            result.onerror=function(error)
            {
                $M.info('【db】执行错误',error);
                fun(false,false)
            }
        }
        else if(fun)
        {
            fun(false,false)
        }
    }
}

//初始化的时候才能创建
$_Db.createTable=function(tableName,opt)
{
    if($_Db.stat=='success' && !this.dbBase.objectStoreNames.contains(tableName))
    {
        $M.info('【db】数据表以被初始化,但此表未被创建')
        return;
    }
    if (!this.dbBase.objectStoreNames.contains(tableName)) 
    {
        $M.info("【db】需要创建一个新的表",tableName);
        //如果表格不存在，创建一个新的表格（keyPath，主键 ； autoIncrement,是否自增），会返回一个对象（objectStore）
        var objectStore = this.dbBase.createObjectStore
        (
            tableName,{keyPath: "id", autoIncrement: true}
        );
        if(opt)
        {
            var i
            for (i in opt) 
            {
                opt[i].unique=(opt[i].unique===false)?opt[i].unique:true;
                objectStore.createIndex
                (
                    opt[i].name,opt[i].name,opt[i]
                );
            }
        }
    }
}

$_Db.clearTable=function(tableName,fun)
{
    function ex(objectStore)
    {
        return objectStore.clear();
    }
    return this.fun(ex,tableName,fun)
}

$_Db.clearTable=function(tableName,fun)
{
    function ex(objectStore)
    {
        return objectStore.clear();
    }
    return this.fun(ex,tableName,fun)
}

$_Db.select=function(tableName,param,index,fun)
{
    return $_Db.poly(tableName,param,null,'select',index,fun);
}

$_Db.selectOne=function(tableName,param,index,fun)
{
    return $_Db.poly(tableName,param,null,'selectOne',index,fun);
}

//返回一个数组，需要对数组里的游标操作。进行监听操作是否成功
$_Db.update=function(tableName,param,data,index,fun)
{
    //param，满足这里面的条件才更新
    return $_Db.poly(tableName,param,data,'update',index,fun);
}

//返回一个数组，需要对数组里的游标操作。进行监听操作是否成功
//有则更新，无则插入
$_Db.replace=function(tableName,param,data,index,fun)
{
    //param，那些字段的值重复，则更新。data为数据
    var _key
    for (_key in param) 
    {
        if(param[_key]===undefined)
        {
            param[_key]=data[_key];
        }
    }

    $_Db.selectOne
    (
        tableName,param,function(result)
        {
            if(result && result.length)
            {
                $_Db.poly(tableName,param,data,'update',index,fun);
            }
            else
            {
                if(!fun && (index instanceof Function))fun=index;
                $_Db.add(tableName,data,fun)
            }
        }
    )
}


//返回一个数组，需要对数组里的游标操作。进行监听操作是否成功
$_Db.delete=function(tableName,param,index,fun)
{
    return $_Db.poly(tableName,param,null,'remove',index,fun);
}
 

//数组，限制长度插入，自动剔除早期数据。必须传唯一索引index
//$_Db.limitPush({table:'limit',name:'key',value:'b',data:{a:1},limit:2})
//表未limit，匹配字段key（唯一索引的字段名）的值为b,向字段插入data，limit代表限制数组长度为2
$_Db.limitPush=function(data)
{
    var tableName=data.table
    var param=data.value
    var index=data.name   
    var fun=data.fun 
    var _key='push';//限制那个字段数组的长度,这里固定了push字段
    var _data={push:data['data']};//数据
    _data[index]=param
    var _limit=data['limit']?data['limit']:100;
    $Db.base.get
    (
        tableName,param,index,
        function(array)
        {
            if(array)
            {
                if(!(array[_key] instanceof Array))array[_key]=[];
                var arr=array[_key]
                arr.push(_data[_key]);
                if(arr.length>_limit)
                {
                    var leng=arr.length
                    for (let index = 0; index < leng-_limit; index++) 
                    {
                        arr.shift();
                    }
                }
                $Db.base.update
                (
                    tableName,{},array,{name:index,key:param},fun
                )
            }
            else
            {
                _data.push=[_data.push]
                $Db.base.add(tableName,_data,fun)
            }
        }
    )
}

//表，限制长度插入，自动剔除早期数据。
//$_Db.limitInsert({table:'limit',where:{a:1},data:{a:2},limit:2}) 
//套路和上面差不多，只是可以不传索引。而且数据会分条储存
//where代表限制条件
$_Db.limitInsert=function(data)
{
    var tableName=data.table
    var param=data.where
    var index=data.index  
    var _data=data.data 
    var fun=data.fun 

    if(index instanceof Function)
    {
        fun=index;
        index=null;
    }

    var _list=_data;
    var _limit=data['limit']?data['limit']:100;

    var _index=0
    for (var indexa in param) 
    {
        !function (par,indexa) 
        {
            param[indexa]=function(params) 
            {
                var tag;
                if(par instanceof Function)
                {
                    tag=(params==par());
                }
                else
                {
                    tag=(params==par);
                }
                if(tag)
                {
                    if(_index>=(_limit-1))
                    {
                        return true;
                    }
                    _index+=1
                }
            }            
        }(param[indexa],indexa)
    }

    $Db.base.delete
    (
        tableName,param,index,
        function(array)
        {
            $Db.base.add(tableName,_list,fun)
        }
    )
}



$_Db.poly=function(tableName,param,update,type,index,fun)
{
    if(!fun && (index instanceof Function))
    {
        fun=index;
        index=undefined;
    }
    
    var arr=[];
    var that=this
    function back(cursor,tag)
    {
        var key,keyu,tag,make,rt
        if(cursor && tag)
        {
            try 
            {
                if(cursor.value)
                {
                    for (key in param) 
                    {
                        if(param[key] instanceof Function)
                        {
                            rt=param[key](cursor.value[key],cursor.value)
                            if(!rt)
                            {
                                if(rt===false)
                                {
                                    if(fun)fun(arr);
                                    return arr;
                                }
                                tag=false;
                                break;
                            }
                        }
                        else
                        {
                            if(cursor.value[key]!=param[key])
                            {
                                tag=false;
                                break;
                            }
                        }
                    }
                    if(tag!==false)
                    {
                        switch (type) 
                        {
                            case 'select':
                                arr.push(cursor.value);
                            break;
                            case 'selectOne':
                                arr.push(cursor.value);
                                if(fun)fun(arr[0]);
                                return arr[0];
                            break;
                            case 'remove':
                                make=cursor.delete();  
                                arr.push(make);      
                            break;
                            case 'update':
                                if(fun)fun(arr);
                                if(!update)return arr;
                                for (keyu in update) 
                                {
                                    if(update[keyu] instanceof Function)
                                    {
                                        cursor.value[keyu]=update[keyu](cursor.value[keyu],cursor.value);
                                    }
                                    else
                                    {
                                        cursor.value[keyu]=update[keyu];
                                    } 
                                }
                                make=cursor.update(cursor.value);
                                arr.push(make);
                            break;
                            default:
                                arr.push(cursor.value);
                            break;
                        }
                    }
                    tag=null;
                }
                cursor.continue();
            } 
            catch (error) 
            {
                if(fun)fun(arr);
                $M.info('【db】游标处理发生意外',error);
            } 
        }
        else
        {
            if(fun)fun(arr);
            $M.info('【db】游标结束');
        }
        return arr;
    }
    if(fun)
    {
        return that.cursor(tableName,index,back);
    }
    else
    {
        return new Promise
        (
            function(reslove,reject)
            {
                fun=reslove
                that.cursor(tableName,index,back);
            }
        )
    }
}


//私有的
$_Db.cursor=function(tableName,index,fun)
{
    function ex(objectStore)
    {
        //{name:'索引名',value:'索引值，可以为数组(框架解析为...arr)',type:'索引操作类型可不传',order:'排序'}

        var ki
        // * 范围：
        // *（1）匹配等于指定键值的记录：var range = IDBKeyRange.only(指定键值)
        // *（2）匹配小于指定键值的记录：var range = IDBKeyRange.lowerBound(指定键值, 是否不包括指定键值)
        // *（3）匹配大于指定键值的记录：var range = IDBKeyRange.upperBound(指定键值, 是否不包括指定键值)
        // *（4）匹配指定范围内的记录：var range = IDBKeyRange.bound(下限键值，上限键值，是否不包括下限键值，是否不包括上限键值
        // 例如： 只取得当前索引的值为110的数据 IDBKeyRange.only("110");
        // 只取得当前索引的值大于110，并且不包括110的数据
        // －－－－－－IDBKeyRange.lowerBound("110", true);
        // 只取得当前索引的值小于110，并且包括110的数据
        // －－－－－－IDBKeyRange.upperBound("110", false);
        // 取得当前索引的值介于110和120之间，并且包括110，但不包括120的数据 
        // －－－－－IDBKeyRange.bound("110", "120", false, true);
        var order
        if(index)
        {
            order=index.order
            var pd=typeof index.value
            if(pd=='string' || pd=='number')
            {
                ki=IDBKeyRange.only(index.value)
            }
            else if(index.type)
            {
                ki=IDBKeyRange[index.type](...index.value)
            }
        }
        
        // * 顺序参数：
        // * next，顺序循环； －－ 默认
        // * nextunique，顺序循环且键值不重复；
        // * prev，倒序循环；
        // * prevunique，倒序循环且键值不重复。
        return objectStore.openCursor(ki,order?order:'next');
    }
    return this.fun(ex,tableName,index,fun)
}

//添加数据
$_Db.add=function(tableName,param,fun)
{
    function ex(objectStore)
    {
        var key;
        for (key in param) 
        {
            if(param[key] instanceof Function)
            {
                param[key]=param[key]();
            }
        }
        return objectStore.add(param);
    }
    return this.fun(ex,tableName,fun)
}

//以主键删除数据
$_Db.remove=function(tableName,id,fun)
{
    function ex(objectStore)
    {
        return objectStore.delete(id);
    }
    return this.fun(ex,tableName,fun)
}

//以主键更新数据
$_Db.put=function(tableName,id,param,fun)
{
    function ex(objectStore)
    {
        return objectStore.put(param,id);
    }
    return this.fun(ex,tableName,fun)
}

//得到数据
$_Db.get=function(tableName,id,index,fun)
{
    //当传入index时，可以用index字段的值来查找
    //index字段必须为索引字段
    function ex(objectStore)
    {
        return objectStore.get(id);
    }
    return this.fun(ex,tableName,index,fun)
}

//得到全部数据
$_Db.getAll=function(tableName,fun)
{
    function ex(objectStore)
    {
        return objectStore.getAll();
    }
    return this.fun(ex,tableName,fun)
}

$_Db.close=function(db)
{
    if(!db)
    {
        $M.info('[db]请等待数据库打开');
        return;
    }
    db.close();
}

$Db.base

$Db.init=function(name, version,fun,fun1)
{
    //version,需要修改数据表，或者修改索引时。需要改版本号。
    //相同的表名，不会重复创建。不会删除之前版本的数据
    var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
    if(!indexedDB)
    {
        $M.info("【db】你的浏览器不支持");
    }
    var request = indexedDB.open(name, version);
    request.onerror = function(e) 
    {
        $M.info('【db】',e.currentTarget.error.message);
    };

    request.onsuccess = function(e) 
    {
        $_Db.stat='success';
        $_Db.dbBase=e.target.result;
        $Db.base=$_Db;
        if(fun)fun($_Db);
        if($Db.initFun && $Db.initFun.length)
        {
            for (var index = 0; index < $Db.initFun.length; index++) 
            {
                if($Db.initFun[index] instanceof Function)
                {
                    $Db.initFun[index]($_Db); 
                }
            }
        }
        $M.info('【db】成功打开');
    };
    //onupgradeneeded事件在第一次打开页面初始化数据库时会被调用，或在当有版本号变化时。
    //所以，你应该在onupgradeneeded函数里创建你的存储数据。如果没有版本号变化，而且页面之前被打开过，你会获得一个onsuccess事件。
    request.onupgradeneeded = function(e) 
    {
        $_Db.stat='upgradeneeded';
        $_Db.dbBase=e.target.result;
        if(fun1)fun1($_Db);
        $M.info('【db】数据库版本更改为： ' + version);
    };
}

$Db.deleteDb=function(name)
{
    if(!name)
    {
        $M.info('[db]请输入数据库名称');
        return;
    }
    try 
    {
        indexedDB.deleteDatabase(name);
    } 
    catch (error) 
    {
        $M.info('[db]删除数据库失败',name);
    }
}
