import DataUtils from '../utils/DataUtils';
import VerifyUtils from '../utils/VerifyUtils';

/**
 * 定义IndexedDB提供类
 *
 * @alias IndexedDBProvider
 *
 * @summary <用于IndexedDB仓库的操作>
 */
export default class IndexedDBProvider {
  /**
   * 构造函数
   * @param {Object} options -仓库选项
   */
  constructor(options) {
    this._dbName = DataUtils.default(options.dbName, 'geodb');

    this._indexDb = null;
    this._db = null;
  }

  /**
   * 打开数据库
   * @param {String} name -数据库名称
   * @param {Number} version -版本
   */
  openDB(name = null, version = 1) {
    if (!this._indexDb) {
      this._indexDb =
        window.indexedDB ||
        window.mozIndexedDB ||
        window.webkitIndexedDB ||
        window.msIndexedDB;
    }
    name = name || this._dbName;

    return new Promise((resolve, reject) => {
      // 兼容浏览器
      var indexedDB =
        window.indexedDB ||
        window.mozIndexedDB ||
        window.webkitIndexedDB ||
        window.msIndexedDB;

      // 打开数据库，若没有则会创建
      const request = indexedDB.open(name, version);
      // 打开成功回调
      request.onsuccess = (event) => {
        this._db = event.target.result // 数据库对象
        resolve({msg: 'success', data: 'open'});
      };
      // 打开失败的回调
      request.onerror = (event) => {
        reject({msg: 'error', data: event.target.error.message})
      };

      // 数据库有更新的回调
      request.onupgradeneeded = (event) => {
        this._db = event.target.result // 数据库对象
        resolve({msg: 'success', data: 'upgradeneeded'});
      }
    });
  }

  /**
   * 创建仓库
   * @param {String} name -仓库名称
   * @param {Object} options -选项
   *
   * @summery <options参数：
   *   primarykey: primarykey,
   *   indexes: [
   *     {name: name, key: key, unique: false},
   *     {name: name, key: key, unique: false}, ...
   *   ]
   * >
   */
  createStore(name, options = {primarykey: 'gid', indexes: []}) {
    const db = this._db;
    if (!db || db.objectStoreNames.contains(name)) return;

    // 创建仓库(表), 并指定主键(keyPath)
    let store = db.createObjectStore(name, {
      keyPath: options.primarykey
    });

    // 创建索引
    if (options.indexes && options.indexes.length > 0) {
      options.indexes.forEach((index) => {
        store.createIndex(index.name, index.key, {unique: index.unique});
      });
    }

    return store;
  }

  /**
   * 获取仓库(表)
   * @param {String} name -仓库名称
   *
   * @return {Object}
   */
  getStore(name, rw = 'readwrite') {
    if (!this._db) return null;

    return this._db.transaction([name], rw).objectStore(name);
  }

  /**
   * 仓库是否已创建
   * @param {String} name -仓库名称
   *
   * @return {Boolean}
   */
  hasStore(name) {
    return (this._db && this._db.objectStoreNames.contains(name));
  }

  /**
   * 添加数据
   * @param {String} storeName -仓库名称
   * @param {Object} data -数据
   */
  addData(storeName, data) {
    return new Promise((resolve, reject) => {
      const store = this.getStore(storeName);
      if (!store) {
        reject({msg: 'error', data: 'objectStore not exist'});
      }

      if (VerifyUtils.isObject(data)) {
        // 添加对象
        let request = store.add(data);
        request.onsuccess = (event) => {
          resolve({msg: 'success', data: 1})
        }
        request.onerror = (event) => {
          reject({msg: 'error', data: event.target.error.message})
        }
      } else {
        reject({msg: 'error', data: 'data not support'});
      }
    });
  }

  /**
   * 更新数据
   * @param {String} storeName -仓库名称
   * @param {Object} data -数据
   */
  updateData(storeName, data) {
    return new Promise((resolve, reject) => {
      const store = this.getStore(storeName);
      if (!store) {
        reject({msg: 'error', data: 'objectStore not exist'});
      }

      let request;
      if (VerifyUtils.isObject(data)) {
        // 更新对象
        request = store.put(data);
        request.onsuccess = (event) => {
          resolve({msg: 'success', data: 1})
        }
        request.onerror = (event) => {
          reject({msg: 'error', data: event.target.error.message})
        }
      } else {
        reject({msg: 'error', data: 'data not support'});
      }
    });
  }

  /**
   * 批量添加数据(废弃)
   * @param {String} storeName -仓库名称
   * @param {Object|Array} data -数据
   */
  addDataBatch(storeName, data) {
    return new Promise((resolve, reject) => {
      const store = this.getStore(storeName);
      if (!store) {
        reject({msg: 'error', data: 'objectStore not exist'});
      }

      if (VerifyUtils.isArray(data)) {
        let request;
        for (let i = 0; i < data.length; i++) {
          request = store.add(data[i]); // 添加对象
          request.onerror = (event) => {
            reject({msg: 'error', data: event.target.error.message})
          }
        }

        resolve({msg: 'success', data: data.length});
      } else {
        reject({msg: 'error', data: 'data not support'});
      }
    });
  }

  /**
   * 批量更新数据
   * @param {String} storeName -仓库名称
   * @param {Object|Array} data -数据
   */
  updateDataBatch(storeName, data) {
    return new Promise((resolve, reject) => {
      try {
        const transaction = this._db.transaction(storeName, 'readwrite');
        const store = transaction.objectStore(storeName);
        if (!store) {
          reject({msg: 'error', data: 'objectStore not exist'});
        }

        if (VerifyUtils.isArray(data)) {
          // 更新对象
          data.forEach(item => store.put(item));

          // Event delegation
          // IndexedDB events bubble: request → transaction → database.
          transaction.oncomplete = function() {
            resolve({msg: 'success', data: data.length});
          }
          transaction.onabort = function (event) {
            reject({msg: 'error', data: event.target.error.message})
          }
        } else {
          reject({msg: 'error', data: 'data not support'});
        }
      } catch (err) {
        reject({msg: 'error', data: err});
      }
    });
  }

  /**
   * 删除数据
   * @param {String} storeName -仓库名称
   * @param {Number} id -主键值
   */
  deleteData(storeName, id) {
    return new Promise((resolve, reject) => {
      const store = this.getStore(storeName);
      if (!store) {
        reject({msg: 'error', data: 'objectStore not exist'});
      }

      let request = store.delete(id);
      request.onsuccess = (event) => {
        resolve({msg: 'success', data: 1});
      }
      request.onerror = (event) => {
        reject({msg: 'error', data: event.target.error.message})
      }
    });
  }

  /**
   * 清空数据
   * @param {String} storeName -仓库名称
   */
  clearData(storeName) {
    return new Promise((resolve, reject) => {
      const store = this.getStore(storeName);
      if (!store) {
        reject({msg: 'error', data: 'objectStore not exist'});
      }

      let request = store.clear();
      request.onsuccess = (event) => {
        resolve({msg: 'success', data: 1})
      }
      request.onerror = (event) => {
        reject({msg: 'error', data: event.target.error.message})
      }
    });
  }

  /**
   * 根据主键获取数据
   * @param {String} storeName -仓库名称
   * @param {Number} id -主键值
   */
  getData(storeName, id) {
    return new Promise((resolve, reject) => {
      const store = this.getStore(storeName, 'readonly');
      if (!store) {
        reject({msg: 'error', data: 'objectStore not exist'});
      }

      let resultArr = [];
      // 根据主键查找数据
      let request = store.get(id);
      request.onsuccess = (event) => {
        const result = event.target.result;
        if (result) {
          resultArr.push(event.target.result);
          resolve({msg: 'success', data: resultArr});
        } else {
          resolve({msg: 'success', data: null})
        }
      }
      request.onerror = (event) => {
        reject({msg: 'error', data: event.target.error.message})
      }
    });
  }

  /**
   * 获取第一条数据
   * @param {String} storeName -仓库名称
   */
  getFirst(storeName) {
    return new Promise((resolve, reject) => {
      const store = this.getStore(storeName, 'readonly');
      if (!store) {
        reject({msg: 'error', data: 'objectStore not exist'});
      }

      // 根据索引查找数据
      let request = store.openCursor();
      request.onsuccess = (event) => {
        const result = event.target.result;
        if (result && result.value) {
          resolve({msg: 'success', data: result.value});
        } else {
          resolve({msg: 'success', data: undefined})
        }
      }
      request.onerror = (event) => {
        reject({msg: 'error', data: event.target.error.message})
      }
    });
  }

  /**
   * 根据索引值(单)获取数据
   * @param {String} storeName -仓库名称
   * @param {String} name -索引名称
   * @param {String|Number} value -索引值
   */
  getDataByIndex(storeName, name, value) {
    return new Promise((resolve, reject) => {
      const store = this.getStore(storeName, 'readonly');
      if (!store) {
        reject({msg: 'error', data: 'objectStore not exist'});
      }

      let resultArr = [];

      // 查找索引
      let indexFetch = store.index(name);
      // 根据条件打开游标
      let request = indexFetch.openCursor(IDBKeyRange.only(value));

      request.onsuccess = (event) => {
        let cursor = event.target.result
        if (cursor) {
          resultArr.push(cursor.value)
          cursor.continue()
        } else {
          resolve({msg: 'success', data: resultArr});
        }
      }
      request.onerror = (event) => {
        reject({msg: 'error', data: event.target.error.message})
      }
    });
  }

  /**
   * 根据索引值(多)获取数据
   * @param {String} storeName -仓库名称
   * @param {String} name -索引名称
   * @param {Array} values -索引值数组
   */
  getDataByIndexes(storeName, name, values) {
    return new Promise((resolve, reject) => {
      const store = this.getStore(storeName, 'readonly');
      if (!store) {
        reject({msg: 'error', data: 'objectStore not exist'});
      }

      let resultArr = [];
      // 查找索引
      let indexFetch = store.index(name);
      let request = null;

      for (let i = 0; i < values.length; i++) {
        // 根据条件打开游标
        request = indexFetch.openCursor(IDBKeyRange.only(values[i]));

        request.onsuccess = (event) => {
          let cursor = event.target.result
          if (cursor) {
            resultArr.push(cursor.value)
            cursor.continue()
          } else {
            if (i === values.length - 1) {
              resolve({msg: 'success', data: resultArr});
            }
          }
        }
        request.onerror = (event) => {
          reject({msg: 'error', data: event.target.error.message})
        }
      }
    });
  }

  /**
   * 获取所有数据
   * @param {String} storeName -仓库名称
   */
  getAllData(storeName) {
    return new Promise((resolve, reject) => {
      const store = this.getStore(storeName, 'readonly');
      if (!store) {
        reject({msg: 'error', data: 'objectStore not exist'});
      }

      // 打开游标
      let request = store.openCursor();

      let resultArr = [];
      request.onsuccess = (event) => {
        let cursor = event.target.result
        if (cursor) {
          resultArr.push(cursor.value)
          cursor.continue()
        } else {
          resolve({msg: 'success', data: resultArr});
        }
      }
      request.onerror = (event) => {
        reject({msg: 'error', data: event.target.error.message})
      }
    });
  }

  /**
   * 关闭数据库
   */
  closeDB() {
    if (this._db) {
      this._db.close();
    }
  }

  /**
   * 删除数据库
   * @param {String} name -数据库名称
   */
  deleteDB(name = 'geodb') {
    if (!this._indexDb) return;

    let deleteRequest = this._indexDb.deleteDatabase(name);
    deleteRequest.onerror = function (event) {
      console.log({msg: 'error', data: event.target.error.message});
    };
    deleteRequest.onsuccess = function (event) {
      console.log({msg: 'success', data: null});
    };
  }
}
