/*
All material copyright Esri, All Rights Reserved, unless otherwise specified.
See https://js.arcgis.com/5.0/esri/copyright.txt for details.
*/
import e from"../../../../core/Error.js";import{onAbort as t,throwIfAborted as s}from"../../../../core/promiseUtils.js";const r=14;class o{constructor(e,t,s=r){this._version=s,this._db=null,this._quotaReductionPromise=null,this._gcCounter=0,this._hit=0,this._miss=0,this._destroyed=!1,this.gcFrequency=50,this.maxByteSize=1073741824,this.quotaReductionFactor=.2,this._dbName=e,this._storeName=t}init(){return Promise.resolve().then(()=>{const e=indexedDB.open(this._dbName,this._version);return e.onupgradeneeded=t=>{const s=e.result,r=e.transaction,o=s.objectStoreNames.contains(this._storeName)?r.objectStore(this._storeName):s.createObjectStore(this._storeName),i=s.objectStoreNames.contains("last_access")?r.objectStore("last_access"):s.createObjectStore("last_access");i.indexNames.contains("date")||i.createIndex("date","date",{unique:!1}),i.indexNames.contains("byteSize")||i.createIndex("byteSize","byteSize",{unique:!1}),t.oldVersion<this._version&&(o.clear(),i.clear())},n(e)}).then(e=>{this._destroyed?e.close():this._db=e})}destroy(){this._db&&(this._db.close(),this._db=null),this._destroyed=!0}get initialized(){return null!=this._db}getHitRate(){return this._hit/(this._hit+this._miss)}put(t,s){if(null==this._db)return Promise.reject(new e("indexedb:not-initialized","IndexedDB Cache is not initialized"));return(null!=this._quotaReductionPromise?this._quotaReductionPromise:Promise.resolve()).then(()=>this._put(t,s)).catch(e=>{if(e&&"QuotaExceededError"===e.name)return null==this._quotaReductionPromise&&(this._quotaReductionPromise=this._getCacheSize().then(e=>this._removeLeastRecentlyAccessed(s.byteSize+Math.ceil(e*this.quotaReductionFactor))),this._quotaReductionPromise.then(()=>this._quotaReductionPromise=null,()=>this._quotaReductionPromise=null)),this._quotaReductionPromise.then(()=>this._put(t,s));throw e}).then(()=>{this._gcCounter--,this._gcCounter<0&&null!=this._db&&(this._gcCounter=this.gcFrequency,this._getCacheSize().then(e=>this._removeLeastRecentlyAccessed(e-this.maxByteSize)))})}get(e,r){const o=this._db;if(null==o)return Promise.resolve(void 0);let i=null;return Promise.resolve().then(()=>{const s=o.transaction(this._storeName,"readonly");i=t(r,()=>{s.abort()});return n(s.objectStore(this._storeName).get(e))}).then(t=>{if(null==t)++this._miss;else if(this._db){++this._hit;this._db.transaction("last_access","readwrite").objectStore("last_access").put({date:Date.now(),byteSize:t.byteSize},e)}return null!=i&&i.remove(),t}).catch(()=>{++this._miss,s(r),null!=i&&i.remove()})}remove(e){const t=this._db;return null==t?Promise.resolve():Promise.resolve().then(async()=>{const s=t.transaction([this._storeName,"last_access"],"readwrite"),r=s.objectStore(this._storeName),o=s.objectStore("last_access"),c=r.delete(e),a=o.delete(e);await Promise.all([n(c),n(a),i(s)])})}_put(e,t){const s=this._db;if(null==s)return Promise.resolve();const r=s.transaction([this._storeName,"last_access"],"readwrite"),o=r.objectStore(this._storeName),c=r.objectStore("last_access"),a=o.put(t,e),l=c.put({date:Date.now(),byteSize:t.byteSize},e);return Promise.all([n(a),n(l),i(r)])}_removeLeastRecentlyAccessed(e){if(e<=0||!this._db)return Promise.resolve();const t=this._db.transaction([this._storeName,"last_access"],"readwrite"),s=t.objectStore(this._storeName),r=t.objectStore("last_access");let o=0;const n=r.index("date").openCursor(null,"next");return n.onsuccess=()=>{const t=n.result;null!=t&&(null!=t.value.byteSize&&(o+=t.value.byteSize),s.delete(t.primaryKey),r.delete(t.primaryKey),o<e&&t.continue())},i(t)}_getCacheSize(){const e=this._db;if(null==e)return Promise.resolve(0);const t=e.transaction("last_access"),s=t.objectStore("last_access");let r=0;const o=s.index("byteSize").openKeyCursor();return o.onsuccess=()=>{const e=o.result;if(!e)return;const t=e.key;null!=t&&(r+=t),e.continue()},i(t).then(()=>r)}}function i(e){return new Promise((t,s)=>{e.oncomplete=()=>t(),e.onerror=()=>s(e.error),e.onabort=()=>s(e.error)})}function n(e){return new Promise((t,s)=>{"done"===e.readyState?null!=e.error?s(e.error):t(e.result):(e.onsuccess=()=>t(e.result),e.onerror=()=>s(e.error))})}export{o as IDBCache,n as whenRequest,i as whenTransaction};
