/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { info } from '../../log/Log';

/**
 * 数据缓存期限
 */
const file_cache_due = 24 * 60 * 60 * 1000;
const db_version = 6;
/**
 * 初始化indexed db
 */
export function initIndexedDB(): Promise<IDBDatabase> {
  return new Promise((resolve, reject) => {
    let request = indexedDB.open('smart_perf', db_version);
    request.onerror = function (event): void {};
    request.onsuccess = function (event): void {
      let db = request.result;
      resolve(db);
    };
    request.onupgradeneeded = function (event): void {
      // @ts-ignore
      let db = event!.target!.result;
      if (db.objectStoreNames.contains('trace_file')) {
        info('delete trace_file table');
        db.deleteObjectStore('trace_file');
      }
      info('create trace_file table');
      let objectStore = db.createObjectStore('trace_file', { keyPath: 'file_index' });
      objectStore.createIndex('file_no', 'file_no');
      objectStore.createIndex('file_id', 'file_id');
      objectStore.createIndex('file_time', 'file_time');
      objectStore.createIndex('file_buffer', 'file_buffer');
    };
  });
}

/**
 * 删除过期数据
 * @param db
 */
export function deleteExpireData(db: IDBDatabase): void {
  if (db?.objectStoreNames.contains('trace_file')) {
    let objectStore = db.transaction(['trace_file'], 'readwrite').objectStore('trace_file');
    let request = objectStore.getAll();
    request.onsuccess = function (event): void {
      let now = new Date().getTime();
      for (let re of request.result) {
        if (now - re.file_time > file_cache_due) {
          objectStore.delete(re.file_index);
        }
      }
      db.close();
    };
    request.onerror = function (): void {
      info('delete expire data failed');
    };
  }
}

/**
 * 缓存数据
 * @param db 数据库链接对象
 * @param oldFileId 上次打开的文件id
 * @param fileId 当前打开的文件id
 * @param buffer 二进制数据
 */
export function cacheTraceFileBuffer(db: IDBDatabase, oldFileId: string, fileId: string, buffer: ArrayBuffer): void {
  if (db?.objectStoreNames.contains('trace_file')) {
    let objectStore = db.transaction(['trace_file'], 'readwrite').objectStore('trace_file');
    let request = objectStore.index('file_id').getAll(oldFileId);
    request.onsuccess = function (event): void {
      for (let re of request.result) {
        objectStore.delete(re.file_index);
      }
      info('delete file success');
      let size = buffer.byteLength;
      let index = 0;
      let no = 0;
      let time = new Date().getTime();
      while (index < size) {
        let sliceLen = Math.min(size - index, 4 * 1024 * 1024);
        objectStore.add({
          file_index: randomUUID(),
          file_no: no,
          file_id: fileId,
          file_time: time,
          file_buffer: buffer.slice(index, index + sliceLen),
        });
        no++;
        index += sliceLen;
      }
      info('cache file success', fileId, buffer.byteLength);
      db.close();
    };
    request.onerror = function (ev): void {
      info('delete error', fileId);
    };
  }
}

function randomUUID(): string {
  // @ts-ignore
  return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c: unknown) =>
    //@ts-ignore
    (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16)
  );
}

/**
 * 获取当前文件的 二进制数据
 * @param fileId
 */
export function getTraceFileBuffer(fileId: string): Promise<ArrayBuffer | null> {
  return new Promise((resolve) => {
    if (fileId === null || fileId === undefined || fileId === '') {
      resolve(new Uint8Array(0).buffer);
    } else {
      initIndexedDB().then((db) => {
        if (db?.objectStoreNames.contains('trace_file')) {
          let request = db
            .transaction(['trace_file'], 'readwrite')
            .objectStore('trace_file')
            .index('file_id')
            .getAll(fileId);
          request.onsuccess = function (ev): void {
            let totalLen = 0;
            let arr = request.result.sort((a, b) => a.file_no - b.file_no);
            for (let re of arr) {
              totalLen += re.file_buffer.byteLength;
            }
            let buffer = new Uint8Array(totalLen);
            let offset = 0;
            for (let re of arr) {
              let ua = new Uint8Array(re.file_buffer);
              buffer.set(ua, offset);
              offset += re.file_buffer.byteLength;
            }
            arr.length = 0;
            request.result.length = 0;
            resolve(buffer.buffer);
          };
        } else {
          resolve(new Uint8Array(0).buffer);
        }
      });
    }
  });
}