text: import {Apis} from "bitsharesjs-ws";
  import idb_helper from "idb-helper";
  import iDBRoot from "idb-root";
  
  const DB_VERSION = 2; // Initial value was 1
  const DB_PREFIX = "graphene_v2";
  const WALLET_BACKUP_STORES = [
      "wallet", "private_keys", "linked_accounts"
  ];
  const MAIN_NET_CHAINID = "dfed6e6fb7a0a9d2211dad2f0bbce9196e72f61aee3999879d524f529612bb0c";
  
  
  var current_wallet_name = "default";
  
  var upgrade = function(db, oldVersion) {
      // DEBUG console.log('... upgrade oldVersion',oldVersion)
      if (oldVersion === 0) {
          db.createObjectStore("wallet", { keyPath: "public_name" })
          idb_helper.autoIncrement_unique(db, "private_keys", "pubkey")
          db.createObjectStore("linked_accounts", { keyPath: "name" })
      }
      if (oldVersion < 2) {
          // Cache only, do not backup...
          db.createObjectStore("cached_properties", { keyPath: "name" })
      }
  }
  
  /**
      Everything in this class is scopped by the database name.  This separates
      data per-wallet and per-chain.
  */
  var getDatabaseName = function(
      current_wallet = current_wallet_name,
      chain_id = __DEPRECATED__ ? MAIN_NET_CHAINID : Apis.instance().chain_id) {
      return [
          DB_PREFIX,
          chain_id ? chain_id.substring(0, 6) : "",
          current_wallet
      ].join("_")
  }
  
  var openDatabase = function(database_name = this.getDatabaseName()) {
      return new Promise((resolve, reject) => {
  
          var openRequest = iDB.impl.open(database_name, DB_VERSION);
  
          openRequest.onupgradeneeded = function (e) {
              // DEBUG console.log('... openRequest.onupgradeneeded ' + database_name)
              // Don't resolve here, indexedDb will call onsuccess or onerror next
              upgrade(e.target.result, e.oldVersion)
          };
  
          openRequest.onsuccess = function (e) {
              // DEBUG console.log('... openRequest.onsuccess ' + database_name, e.target.result)
              var db = e.target.result
              iDB.database_name = database_name
              idb_helper.set_graphene_db(db)
              resolve(db);
          };
  
          openRequest.onerror = function (e) {
              // DEBUG console.log("... openRequest.onerror " + database_name,e.target.error, e)
              reject(e.target.error);
          };
      })
  }
  
  var iDB = (function () {
  
      var _instance;
      var idb;
  
      /** Be carefull not to call twice especially for a new database
         needing an upgrade...
      */
      function openIndexedDB(chain_id) {
          return iDB.root.getProperty("current_wallet", "default").then(current_wallet => {
              current_wallet_name = current_wallet;
              var database_name = getDatabaseName(current_wallet, chain_id);
              return openDatabase(database_name);
          });
      }
  
      function init(chain_id) {
          let promise = openIndexedDB(chain_id);
          promise.then(db => {
              idb = db;
          });
          return {
              init_promise: promise,
              db: () => idb
          };
      }
  
      return {
          WALLET_BACKUP_STORES,
          getDatabaseName: getDatabaseName,
          getCurrentWalletName: ()=> current_wallet_name,
          deleteDatabase: function(are_you_sure = false) {
              if( ! are_you_sure) return "Are you sure?"
              console.log("deleting", this.database_name)
              var req = iDB.impl.deleteDatabase(this.database_name)
              return req.result
          },
  
          set_impl: function(impl) {
              this.impl = impl
              this.root = new iDBRoot(this.impl)
          },
  
          set_chain_id: function(chain_id) {
              this.chain_id = chain_id
              var chain_substring = chain_id ? chain_id.substring(0, 6) : ""
              this.root.setDbSuffix("_" + chain_substring)
          },
  
          init_instance: function (
              indexedDBimpl,
              chain_id = __DEPRECATED__ ? MAIN_NET_CHAINID : Apis.instance().chain_id
          ) {
              if (!_instance) {
                  if(indexedDBimpl) {
                      this.set_impl( indexedDBimpl )
                      if("__useShim" in indexedDBimpl) {
                          this.impl.__useShim() //always use shim
                      }
                  }
                  this.set_chain_id(chain_id)
                  _instance = init(chain_id)
              }
              return _instance;
          },
  
          instance: function () {
              if (!_instance) {
                  throw new Error("Internal Database instance is not initialized");
              }
              return _instance;
          },
  
          close: function () {
              if (_instance && _instance.db()) _instance.db().close();
              idb_helper.set_graphene_db(null);
              _instance = undefined;
          },
  
          add_to_store: function (store_name, value) {
              return new Promise((resolve, reject) => {
                  let transaction = this.instance().db().transaction([store_name], "readwrite");
                  let store = transaction.objectStore(store_name);
                  let request = store.add(value);
                  request.onsuccess = () => { resolve(value); };
                  request.onerror = (e) => {
                      console.log("ERROR!!! add_to_store - can't store value in db. ", e.target.error.message, value);
                      reject(e.target.error.message);
                  };
              });
          },
          remove_from_store: function (store_name, value) {
              return new Promise((resolve, reject) => {
                  let transaction = this.instance().db().transaction([store_name], "readwrite");
                  let store = transaction.objectStore(store_name);
                  let request = store.delete(value);
                  request.onsuccess = () => { resolve(); };
                  request.onerror = (e) => {
                      console.log("ERROR!!! remove_from_store - can't remove value from db. ", e.target.error.message, value);
                      reject(e.target.error.message);
                  };
              });
          },
          clear_store: function (store_name){
              return new Promise((resolve, reject) => {
                  let transaction = this.instance().db().transaction([store_name], "readwrite");
                  let store = transaction.objectStore(store_name);
                  let request = store.clear();
                  request.onsuccess = () => { resolve(); };
                  request.onerror = (e) => {
                      console.log("ERROR!!! clear_store - can't clear db. ", e.target.error.message, value);
                      reject(e.target.error.message);
                  };
              });
          },
          load_data: function (store_name) {
              return new Promise((resolve, reject) => {
                  let data = [];
                  let transaction = this.instance().db().transaction([store_name], "readonly");
                  let store = transaction.objectStore(store_name);
                  let request = store.openCursor();
                  //request.oncomplete = () => { resolve(data); };
                  request.onsuccess = e => {
                      let cursor = e.target.result;
                      if (cursor) {
                          data.push(cursor.value);
                          cursor.continue();
                      } else {
                          resolve(data);
                      }
                  };
                  request.onerror = (e) => {
                      console.log("ERROR!!! open_store - can't get '`${store_name}`' cursor. ", e.target.error.message);
                      reject(e.target.error.message);
                  };
              });
          },
  
          /** Persisted to disk but not backed up.
              @return promise
          */
          getCachedProperty: function(name, default_value) {
              var db = this.instance().db()
              var transaction = db.transaction(["cached_properties"], "readonly")
              var store = transaction.objectStore("cached_properties")
              return idb_helper.on_request_end( store.get(name) ).then( event => {
                  var result = event.target.result
                  return result ? result.value : default_value
              }).catch( error => { console.error(error); throw error })
          },
  
          /** Persisted to disk but not backed up. */
          setCachedProperty: function(name, value) {
              var db = this.instance().db()
              var transaction = db.transaction(["cached_properties"], "readwrite")
              var store = transaction.objectStore("cached_properties")
              if(value && value["toJS"]) value = value.toJS() //Immutable-js
              return idb_helper.on_request_end( store.put({name, value}) )
                  .catch( error => { console.error(error); throw error })
          },
  
          backup: function (store_names = WALLET_BACKUP_STORES) {
              var promises = []
              for (var store_name of store_names) {
                  promises.push(this.load_data(store_name))
              }
              //Add each store name
              return Promise.all(promises).then( results => {
                  var obj = {}
                  for (let i = 0; i < store_names.length; i++) {
                      var store_name = store_names[i]
                      if( store_name === "wallet" ) {
                          var wallet_array = results[i]
                          // their should be only 1 wallet per database
                          for(let wallet of wallet_array)
                              wallet.backup_date = new Date().toISOString()
                      }
                      obj[store_name] = results[i]
                  }
                  return obj
              })
          },
          restore: function(wallet_name, object) {
              var database_name = getDatabaseName(wallet_name)
              return openDatabase(database_name).then( db => {
                  var store_names = Object.keys(object)
                  var trx = db.transaction(store_names, "readwrite")
                  for(let store_name of store_names) {
                      var store = trx.objectStore(store_name)
                      var records = object[store_name]
                      for(let record of records) {
                          store.put(record)
                      }
                  }
                  return idb_helper.on_transaction_end(trx)
              })
          }
      };
  
  })();
  
  export default iDB;
  