import * as tf from "@tensorflow/tfjs"
import "@tensorflow/tfjs-backend-webgl";
import Queen from "./queen.js"

export default class QueenEngine {
  private queenEngine: any;
  private wasmInvoker: any;
  private inputTextureId: number;
  private queenCanvas: any;
  private appName: string = "queen-web-demo";
  private tensorflowModel: any;
  private sdkUrl: string;
  private sdkLicenseKey: string;
  private materialMap: Map<string, string>;

  constructor() {
    this.inputTextureId = 0;
    this.wasmInvoker = null;
    this.queenEngine = null;
    this.queenCanvas = null;
    this.tensorflowModel = null;
    this.sdkUrl = "";
    this.sdkLicenseKey = "";
    this.materialMap = new Map();

  }

  initialize(sdkUrl: string, sdkLicenseKey: string, initCallback: any, loaddingCallback: any, queenCanvasId: string) {
    const self = this;
    this.sdkUrl = sdkUrl;
    this.sdkLicenseKey = sdkLicenseKey;
    this.initTensorflow();
    this.loadResourceData(loaddingCallback, function (queenData: any) {
      const queenModule_ = {
        onRuntimeInitialized: function () {
          self.wasmInvoker = queenModule_;
          self.queenInitialize(queenCanvasId)?.then(() => {
            if (initCallback !== undefined) {
              initCallback()
            }
          });
        },
        onTensorflowInferenceCallback: function (imageData: ImageData, width: number, height: number, channel: number) {
          const img = tf.browser.fromPixels(imageData);
          const casted = img.toFloat();
          const inputTensor = tf.expandDims(casted);
          const outputTensor = self.tensorflowModel.execute(inputTensor);
          const formatOutput = outputTensor.mul(255);
          let result = Array.from(formatOutput.dataSync());
          tf.dispose([formatOutput, outputTensor, inputTensor, casted, img]);
          let segResult = new Uint8Array(width * height);
          for (let i = 0; i < width * height; i++) {
            segResult[i] = result[i * 2 + 1] as number;
          }
          return segResult;
        },
        onAbort: function (e: string) {
          console.error("wasm error,", e);
        },
        locateFile: function (wasm: string) {
          return self.sdkUrl + "queen.wasm";
        },
        getPreloadedPackage: function (remotePackageName: string, remotePackageSize: number) {
          return queenData;
        },
      };
      Queen(queenModule_);
    }, null);
  }

  async initTensorflow() {
    tf.setBackend('webgl');
    this.tensorflowModel = await tf.loadGraphModel(this.sdkUrl + 'tensorflow/model.json');
  }

  loadResourceData(progressCallback: any, responseCallback: any, errCallback: any) {
    const self = this;
    const remotePackageName = "queen.bin";
    const dataUrl = this.sdkUrl + remotePackageName;
    this.readResourceData(function (queenData: any) {
      if (progressCallback) {
        progressCallback(100);
      }
      responseCallback(queenData);
    }, function () {
      self.fetchRemotePackage(dataUrl, progressCallback, function (queenData: any) {
        if (progressCallback) {
          progressCallback(100);
        }
        self.saveResourceData(queenData);
        responseCallback(queenData);
      }, errCallback);
    });
  }

  fetchRemotePackage(packageName: string, progressCallback: any, responseCallback: any, errCallback: any) {
    const self = this;
    const xhr = new XMLHttpRequest();
    xhr.open('GET', packageName, true);
    xhr.responseType = 'arraybuffer';
    xhr.onprogress = function (event) {
      if (event.loaded) {
        if (event.total && progressCallback) {
          progressCallback(event.loaded / event.total * 100);
        }
      }
    };
    xhr.onerror = function (event) {
      throw new Error("NetworkError for: " + packageName);
    }
    xhr.onload = function (event) {
      if (xhr.status === 200 || xhr.status === 304 || xhr.status === 206 || (xhr.status === 0 && xhr.response)) { // file URLs can return 0
        console.info("xhr.onload", xhr.status);
        const packageData = xhr.response;
        if (responseCallback) {
          responseCallback(packageData);
        }
      } else {
        throw new Error(xhr.statusText + " : " + xhr.responseURL);
      }
    };
    xhr.send(null);
  }

  saveResourceData(queenData: Uint8Array) {
    const storeName = "QueenTable";
    const open = window.indexedDB.open(this.appName + "_queenResV1_16", 1);
    open.onupgradeneeded = function () {
      const db = open.result;
      if (!db.objectStoreNames.contains(storeName)) {
        db.createObjectStore(storeName, { keyPath: "id" });
      }
    };

    open.onsuccess = function () {
      const db = open.result;
      const transaction = db.transaction([storeName], "readwrite");
      const objectStore = transaction.objectStore(storeName);
      const objectStoreRequest = objectStore.put({ id: 1, queenData });
      objectStoreRequest.onsuccess = function (event) {
      };
      objectStoreRequest.onerror = function (event) {
        console.log("queen resource save fail");
      };
    }
  }

  readResourceData = (successCallback: any, errorCallback: any) => {
    const storeName = "QueenTable";
    const open = window.indexedDB.open(this.appName + "_queenResV1_16", 1);
    open.onupgradeneeded = function () {
      const db = open.result;
      if (!db.objectStoreNames.contains(storeName)) {
        db.createObjectStore(storeName, { keyPath: "id" });
      }
    };
    open.onsuccess = function () {
      const db = open.result;
      const transaction = db.transaction([storeName]);
      const objectStore = transaction.objectStore(storeName);
      const request = objectStore.get(1);
      request.onsuccess = function () {
        if (request.result) {
          successCallback(request.result.queenData);
        } else {
          errorCallback()
        }
      };
      request.onerror = function () {
        errorCallback()
      }
    }
    open.onerror = function () {
      errorCallback()
    }
  };


  render(imageData: Uint8Array, imageWidth: number, imageHeight: number, renderCallback: any) {
    const size = imageData.length;
    const imageBufferPtr = this.wasmInvoker._malloc(size);
    this.wasmInvoker.HEAPU8.set(imageData, imageBufferPtr);
    const outImageBufferPtr = this.wasmInvoker._malloc(size);
    this.queenEngine.engineRender(imageBufferPtr, imageWidth, imageHeight, outImageBufferPtr);
    this.wasmInvoker._free(imageBufferPtr);
    this.renderDataCallback(outImageBufferPtr, size, imageWidth, imageHeight, renderCallback);
  }


  /**
   * 
   * @param mediaElement v媒体对象 imageElement、videoElement、canvasElement、videoframe、imagebitmap
   * @param width 
   * @param height 
   * @param renderCallback 
   */
  renderWithMediaObject(mediaElement: any, width: number, height: number, renderCallback: any) {
    if (this.inputTextureId === 0) {
      this.inputTextureId = this.generateTextureId();
    }

    this.updateTexture(this.inputTextureId, mediaElement);
    this.renderWithTextureIdAndBuffer(this.inputTextureId, width, height, renderCallback);
  }

  /**
   * 渲染纹理返回纹理ID
   * @param textureId 
   * @param imageWidth 
   * @param imageHeight 
   * @returns 
   */
  renderTextureId(textureId: number, imageWidth: number, imageHeight: number) {
    const outTextureId = this.queenEngine.engineRenderWithTextureId(textureId, imageWidth, imageHeight, 0);
    return this.wasmInvoker.GL.textures[outTextureId];
  }

  /**
   * 基于纹理ID渲染并返回纹理ID
   * @param mediaElement  媒体对象 imageElement、videoElement、canvasElement、videoframe、imagebitmap
   * @param width 
   * @param height 
   * @returns 
   */
  renderMediaObjectToTexture(mediaElement: any, width: number, height: number) {
    if (this.inputTextureId === 0) {
      this.inputTextureId = this.generateTextureId();
    }

    this.updateTexture(this.inputTextureId, mediaElement);
    let outTextureId = this.queenEngine.engineRenderWithTextureId(this.inputTextureId, width, height, 0);
    return this.wasmInvoker.GL.textures[outTextureId];
  }


  /**
   * 前端JS渲染回调
   * @param outImageBufferPtr 
   * @param size 
   * @param imageWidth 
   * @param imageHeight 
   * @param renderCallback 
   */
  renderDataCallback(outImageBufferPtr: number, size: number, imageWidth: number, imageHeight: number, renderCallback: any) {
    const newImageData = this.wasmInvoker.HEAPU8.subarray(outImageBufferPtr, outImageBufferPtr + size);
    this.wasmInvoker._free(outImageBufferPtr);
    if (renderCallback !== undefined) {
      renderCallback(newImageData, imageWidth, imageHeight);
    }
  }

  /**
   * 基于纹理ID渲染
   * 返回imageData渲染美颜
   * @param textureId 纹理ID
   * @param imageWidth 宽
   * @param imageHeight 高
   * @param renderCallback 图片渲染回调
   */
  renderWithImageData(imageData: Uint8Array, imageWidth: number, imageHeight: number, renderCallback: any) {
    if (this.inputTextureId === 0) {
      this.inputTextureId = this.generateTextureId();
    }
    this.updateTextureData(this.inputTextureId, imageWidth, imageHeight, imageData);
    this.renderWithTextureId(this.inputTextureId, imageWidth, imageHeight, renderCallback)
  }

  /**
   * 基于纹理ID渲染
   * 返回ImageData数据
   * @param textureId 
   * @param imageWidth 
   * @param imageHeight 
   * @param renderCallback 
   */
  renderWithTextureIdAndBuffer(textureId: number, imageWidth: number, imageHeight: number, renderCallback: any) {
    const channels = 4;
    const size = imageWidth * imageHeight * channels;
    const outImageBufferPtr = this.wasmInvoker._malloc(size);
    this.queenEngine.engineRenderWithTextureIdAndBuffer(textureId, imageWidth, imageHeight, outImageBufferPtr);
    this.renderDataCallback(outImageBufferPtr, size, imageWidth, imageHeight, renderCallback);
  }

  /**
   * 基于纹理ID渲染
   * 返回ImageData数据
   * @param textureId 
   * @param imageWidth 
   * @param imageHeight 
   * @param renderCallback 
   */
  renderWithTextureId(textureId: number, imageWidth: number, imageHeight: number, renderCallback: any) {
    const outTextureId = this.queenEngine.engineRenderWithTextureId(textureId, imageWidth, imageHeight, 0);
    const newImageData = this.readPixelsFromWebGL(outTextureId, imageWidth, imageHeight);
    if (renderCallback !== undefined) {
      renderCallback(newImageData, imageWidth, imageHeight);
    }
  }

  generateTextureId() {
    const webglTexture = this.initializeTexture();
    return this.registerNativeTextureId(webglTexture);
  }

  registerNativeTextureId(texture: any) {
    const textureId = this.wasmInvoker.GL.getNewId(this.wasmInvoker.GL.textures);
    this.wasmInvoker.GL.textures[textureId] = texture;
    return textureId;
  }

  getWebgl() {
    return this.queenCanvas.getContext("webgl2") ? this.queenCanvas.getContext("webgl2") : this.queenCanvas.getContext("webgl");
  }

  initializeTexture() {
    const gl = this.getWebgl();
    const texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 0, 255]));
    gl.bindTexture(gl.TEXTURE_2D, null);
    return texture;
  }

  createTexture(imageData: Uint8Array, imageWidth: number, imageHeight: number) {
    if (this.inputTextureId === 0) {
      this.inputTextureId = this.generateTextureId();
    }
    this.updateTextureData(this.inputTextureId, imageWidth, imageHeight, imageData);
    return this.inputTextureId;
  }

  updateTexture(textureId: number, mediaSource: any) {
    const gl = this.getWebgl();
    const texture = this.wasmInvoker.GL.textures[textureId];
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);


    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, mediaSource);
    // gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width,height, 0, gl.RGBA, gl.UNSIGNED_BYTE, mediaData);
  }

  /**
   * 更新纹理
   * @param textureId 纹理ID
   * @param width 宽
   * @param height 高
   * @param imageData 图片数据 
   */
  updateTextureData(textureId: number, width: number, height: number, imageData: Uint8Array) {
    const gl = this.getWebgl();
    const texture = this.wasmInvoker.GL.textures[textureId];
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, imageData);
    gl.bindTexture(gl.TEXTURE_2D, null);
  }

  /**
   * 读取纹理图片
   * @param textureId 纹理ID
   * @param width 
   * @param height 
   * @returns 
   */
  readPixelsFromWebGL(textureId: number, width: number, height: number) {
    const gl = this.getWebgl();
    const pixels = new Uint8Array(width * height * 4);
    const align = 1;
    const texture = this.wasmInvoker.GL.textures[textureId];
    const mBuffer = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, mBuffer);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
    gl.pixelStorei(gl.PACK_ALIGNMENT, align);
    gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    gl.deleteFramebuffer(mBuffer);
    return pixels;
  }

  /**
   * 初始化queen
   * @param canvasId 
   * @returns 
   */
  queenInitialize(canvasId: string) {
    const self = this;
    const engineParam = '[{"action":0,"type":4,"enable":true},{"action":1,"param":3,"value":0.001}]';
    if (this.queenEngine === null) {
      let queenCanvasId = canvasId;
      if (queenCanvasId === undefined) {
        queenCanvasId = "queen_wasm_canvas_effect_render_1";
      }
      this.queenCanvas = document.getElementById(queenCanvasId)
      if (this.queenCanvas === null || this.queenCanvas === undefined) {
        const queenCanvas = document.createElement("canvas");
        queenCanvas.id = queenCanvasId;
        queenCanvas.style.display = "none";
        document.body.appendChild(queenCanvas);
        this.queenCanvas = queenCanvas;
      }
      this.initGLContext(this.queenCanvas);
      this.queenEngine = new this.wasmInvoker.Engine();
      return new Promise<void>(function (resolve, reject) {
        const initCallback = self.wasmInvoker.addFunction(function (result) {
          if (result === 0) {
            resolve();
          } else {
            reject();
          }
        }, 'vi');
        self.queenEngine.engineInit(self.sdkLicenseKey, engineParam, initCallback);
      });
    }
  }

  initGLContext(canvas: any) {
    const webglCtx = this.wasmInvoker.GL.createContext(canvas, {
      explicitSwapControl: 0,
      depth: 1,
      stencil: 1,
      antialias: 1,
      majorVersion: 2,
      minorVersion: 0,
      alpha: 1
    });

    this.wasmInvoker.GL.makeContextCurrent(webglCtx);
  }

  /**
     * 绿幕/蓝幕抠图
     * 开启此抠图功能，纯色背景抠图（setPureColorToBackground）会关闭。
     * @param isBlue 绿幕:false，蓝幕:true
     * @param backgroundImgUrl 背景图片
     */
  setGreenScreenWithUrl = function (isBlue, backgroundImgUrl) {
    const self = this;
    const threshold = 1; //幕布敏感度[1,10]，默认1
    const enableBlue = isBlue;
    const autoThreshold = true; //是否根据环境动态计算幕布敏感度，为true时调节参数threshold失效，为false时调节参数threshold生效
    return new Promise(function (resolve, reject) {
      if (backgroundImgUrl === "") {
        self.queenEngine.setGreenScreen("", enableBlue, threshold, autoThreshold);
        resolve();
      }
      else {
        self.loadResourceFileWithUrlSaveAs(backgroundImgUrl, "bg.png", false).then(backgroundImgPath => {
          self.queenEngine.setGreenScreen(backgroundImgPath, enableBlue, threshold, autoThreshold);
          resolve();
        }, function () { reject() });
      }
    });
  }

  /**
   * 背景模糊
   * @param enableBokeh 是否启用
   */
  enableBokehBackground(enableBokeh: boolean) {
    if (enableBokeh) {
      this.setQueenBeautyType(kQueenBeautyType.BackgroundProcess, true);
      this.queenEngine.setSegmentBackgroundProcessType(0);
    } else {
      this.setQueenBeautyType(kQueenBeautyType.BackgroundProcess, false);
    }
  }

  /**
   * 实景抠图
   * @param backgroundImgUrl 背景图片地址
   * @returns 
   */
  setSegmentBackgroundUrl(backgroundImgUrl: string) {
    const self = this;
    return new Promise<void>(function (resolve, reject) {
      if (backgroundImgUrl === "") {
        self.setSegmentBackgroundImage("");
        resolve();
      } else {
        self.loadResourceFileWithUrlSaveAs(backgroundImgUrl, "bg.png", false).then(backgroundImgPath => {
          self.setSegmentBackgroundImage(backgroundImgPath);
          resolve();
        }, function () { reject() });
      }
    });
  }

  /**
   * 实景AI抠图
   * @param backgroundImgPath SDK中资源路径
   */
  setSegmentBackgroundImage(backgroundImgPath: string) {
    const flipX = false;
    const flipY = false;
    // self.queenEngine.setSegmentBackgroundProcessType(1);
    // self.queenEngine.setAISegmentForegroundPadding(1);

    /** 实景抠像性能模式：
    * 0. 自动模式（默认模式，使用小模型，当节能模式开启时还会进行隔帧检测）
    * 1. 最佳画质模式（使用大模型）
    * 2. 平衡模式（使用小模型）
    * 3. 最佳性能模式（使用小模型 + 隔帧检测）
    */
    this.queenEngine.setSegmentPerformanceMode(3);
    if (backgroundImgPath === "") {
      this.queenEngine.setSegmentBackgroundImage("", flipX, flipY);
    } else {
      this.queenEngine.setSegmentBackgroundImage(backgroundImgPath, flipX, flipY);
    }
  }

  /**
  * 纯色背景抠图。
  * 注意：开启此抠图功能，绿幕抠图（setGreenScreen）会关闭。
  */
  setPureColorToBackground(backgroundImgPath: string) {
    const threshold = 1; //幕布敏感度[1,10]，默认1
    const colorType = 5;
    this.queenEngine.setPureColorToBackground(backgroundImgPath, colorType, threshold);
  }

  /**
   * 显示美颜配置
   */
  showQueenParamJson() {
    const config = this.queenEngine.getQueenBeautyParamsJsonConfig();
    console.info(config);
  }

  /** 
   * 载入url资源到引擎
   * @param url url链接
   * @param needUnZip 是否需要解压
   * @returns 
   */
  loadResourceFileWithUrl = function (url: string, needUnZip: boolean) {
    const self = this;
    return new Promise<string>(function (resolve, reject) {
      if (url === "") {
        resolve(url);
        return;
      }
      const loadCallback = self.wasmInvoker.addFunction(function (fileNamePtr: any, size: number) {
        let fileName = self.wasmInvoker.UTF8ToString(fileNamePtr);
        if (size > 0) {
          resolve(fileName);
        } else {
          reject(fileName);
        }
      }, 'vii');
      const saveAsFileName = "";
      self.wasmInvoker.Resource.loadResourceFileWithUrl(url, saveAsFileName, needUnZip, loadCallback);
    });
  }

  /**
   * 载入url资源到引擎
   * @param url url链接
   * @param saveFileName 保存文件名
   * @param needUnZip 是否需要解压
   * @returns 
   */
  loadResourceFileWithUrlSaveAs = function (url: string, saveFileName: string, needUnZip: boolean) {
    const self = this;
    return new Promise<string>(function (resolve, reject) {
      fetch(url)
        .then(res => res.blob())
        .then(blob => blob.arrayBuffer())
        .then(buffer => {
          const loadCallback = self.wasmInvoker.addFunction(function (fileNamePtr: any, size: number) {
            let fileName = self.wasmInvoker.UTF8ToString(fileNamePtr);
            if (size > 0) {
              resolve(fileName);
            } else {
              reject(fileName);
            }
          }, 'vii');
          const bufferArray = new Uint8Array(buffer);
          const size = bufferArray.length;
          const inputBufferPtr = self.wasmInvoker._malloc(size);
          self.wasmInvoker.HEAPU8.set(bufferArray, inputBufferPtr);
          self.wasmInvoker.Resource.loadResourceFileWithData(inputBufferPtr, size, saveFileName, needUnZip, loadCallback);
          self.wasmInvoker._free(inputBufferPtr);
        });
    });
  }

  /**
   * 验证资源文件是否存在
   * @param fileName 
   * @returns 
   */
  checkResourceFile(fileName: string) {
    return this.wasmInvoker.Resource.checkResourceFile(fileName);
  }

  /**
   * 配置美颜类型
   * @param type kQueenBeautyType枚举
   * @param enable 是否启用
   */
  setQueenBeautyType(type: number, enable: boolean) {
    this.queenEngine.setQueenBeautyType(type, enable);
  }
  /**
   * 配置美颜参数值
   * @param param kQueenBeautyParams枚举
   * @param value 参数值
   */
  setQueenBeautyParams(param: number, value: number) {
    this.queenEngine.setQueenBeautyParams(param, value);
  }

  /**
 * 输入是否进行翻转
 * @param {*} flip 0：不做翻转 1：X轴翻转 2：Y轴翻转
 * @returns 
 */
  setInputFlip(flip: number) {
    this.inputFlip = flip;
    return this.queenEngine.setInputFlip(flip);
  }

  /**
   * 设置Lut滤镜
   * @param imageUrl 
   * @returns 
   */
  setLutImageUrl(imageUrl: string) {
    const self = this;
    return new Promise<void>(function (resolve, reject) {
      if (imageUrl === "") {
        self.setLutImagePath("");
        resolve();
      } else {
        self.loadResourceFileWithUrl(imageUrl, false).then(assetImagePath => {
          self.setLutImagePath(assetImagePath);
          resolve();
        }, function () { reject() });
      }
    });
  }

  /**
  * 设置Lut滤镜
  * @param imagePath 
  * @returns 
  */
  setLutImagePath(imagePath: string) {
    this.queenEngine.setLutImagePath(imagePath);
  }

  //"美型相关API"
  /**
   *  @brief 设置美型类型,设置前需要将kQueenBeautyType.FaceShape打开
   *  @param faceShapeType  需要设置美型的类型，参考QueenBeautyFaceShapeType
   *  @param value 需要设置的值
   */
  setFaceShape(faceShapeType: number, value: number) {
    this.queenEngine.setFaceShape(faceShapeType, value);
  }

  //"美妆相关api"
  /**
  * @brief 设置美妆类型和图片素材路径，设置美妆需要将kQueenBeautyType.Makeup 打开
  * @param makeupType 美妆类型
  * @param imagePaths 美妆素材地址集合
  * @param blend 混合类型
  */
  setMakeupWithUrl(makeupType: number, imageUrl: string, blend: number) {
    const self = this;
    return new Promise<void>(function (resolve, reject) {
      self.loadResourceFileWithUrl(imageUrl, false).then(imagePath => {
        self.setMakeupWithType(makeupType, imagePath, blend);
        resolve();
      }, function () { reject() });
    });
  }

  /**
  * @brief 设置美妆类型和图片素材路径，设置美妆需要将kQueenBeautyType.Makeup 打开
  * @param makeupType 美妆类型
  * @param packageUrl 资源压缩包url
  * @param imageName 包内图片名称
  * @param blend 混合类型
  */
  setMakeupWithPackage(makeupType: number, packageUrl: string, imageName: string, blend: number) {
    console.info("setMakeupWithPackage:", makeupType, packageUrl, imageName, blend);
    const self = this;
    return new Promise<void>(function (resolve, reject) {
      self.loadResourceFileWithUrl(packageUrl, true).then(packageDir => {
        self.setMakeupWithType(makeupType, packageDir + imageName, blend);
        resolve();
      }, function () { reject() });
    });
  }


  /**
  * @brief 设置美妆类型和图片素材路径，设置美妆需要将kQueenBeautyType.Makeup 打开
  * @param makeupType 美妆类型
  * @param imagePaths 美妆素材地址集合
  * @param blend 混合类型
  */
  setMakeupWithType(makeupType: number, imagePaths: string, blend: number) {
    this.setMakeupWithTypeFps(makeupType, imagePaths, blend, 20);
  }

  /**
  * @brief 设置美妆类型和图片素材路径
  * @param makeupType 美妆类型
  * @param imagePaths 美妆素材地址集合（多个资源则,号分开）
  * @param blend 混合类型
  * @param fps 对应的帧率
  */
  setMakeupWithTypeFps(makeupType: number, imagePaths: string, blend: number, fps: number) {
    this.queenEngine.setMakeupWithTypeFps(makeupType, imagePaths, blend, fps);
  }

  /**
  * @brief 设置女性美妆透明度，可指定性别
  * @param makeupType 美妆类型
  * @param alpha 妆容透明度
  */
  setMakeupFemaleAlpha(makeupType: number, alpha: number) {
    this.setMakeupAlphaWithType(makeupType, true, alpha);
  }

  /**
  * @brief 设置男性美妆透明度，可指定性别
  * @param makeupType 美妆类型
  * @param alpha 妆容透明度
  */
  setMakeupMaleAlpha(makeupType: number, alpha: number) {
    this.setMakeupAlphaWithType(makeupType, false, alpha);
  }

  /**
  * @brief 设置美妆透明度，可指定性别
  * @param makeupType 美妆类型
  * @param isFeMale 是否是女性，女性:true，男性:false，（男性为接口预留，这里均传女性即true即可）
  * @param alpha 妆容透明度
  */
  setMakeupAlphaWithType(makeupType: number, isFeMale: boolean, alpha: number) {
    this.queenEngine.setMakeupAlphaWithType(makeupType, isFeMale, alpha);
  }

  /**
  * @brief 设置美妆类型的混合类型
  * @param makeupType 美妆类型
  * @param blend 混合类型
  */
  setMakeupBlendWithType(makeupType: number, blend: number) {
    this.queenEngine.setMakeupBlendWithType(makeupType, blend);
  }

  /**
  * @brief 清除所有美妆
  */
  resetAllMakeupType() {
    this.queenEngine.resetAllMakeupType();
  }

  // "贴纸相关API"
  /**
  * @brief 增加贴纸/贴图/实景抠图需要替换的背景，素材统一接口，支持GLTF,TAOPAI,MEDIAAI 类型
  * @param materialPath 要添加的素材的路径
  */
  addMaterialWithUrl(materialZipUrl: string) {
    const self = this;
    return new Promise<void>(function (resolve, reject) {
      self.loadResourceFileWithUrl(materialZipUrl, true).then(materialFolder => {
        self.materialMap.set(materialZipUrl, materialFolder);
        self.addMaterialWithPath(materialFolder);
        resolve();
      }, function () { reject() });
    });
  }

  /**
  * @brief 增加贴纸/贴图/实景抠图需要替换的背景，素材统一接口，支持GLTF,TAOPAI,MEDIAAI 类型
  * @param materialPath 要添加的素材的路径
  */
  addMaterialWithPath(materialPath: string) {
    this.queenEngine.addMaterialWithPath(materialPath);
  }

  /**
   * @brief 移除头像贴纸
   * @param materialZipUrl 
   */
  removeMaterialWithUrl(materialZipUrl: string) {
    if (this.materialMap.has(materialZipUrl)) {
      const materialFolderPath = this.materialMap.get(materialZipUrl) as string;
      this.removeMaterialWithPath(materialFolderPath);
    }
  }

  /**
  * @brief 删除贴纸/贴图/实景抠图需要替换的背景
  * @param materialPath 要删除的素材的路径
  */
  removeMaterialWithPath(materialPath: string) {
    this.queenEngine.removeMaterialWithPath(materialPath);
  }

  /**
  * @brief 展示人脸识别点位
  * @param show 是否展示
  */
  showFaceDetectPoint(show: boolean) {
    this.queenEngine.showFaceDetectPoint(show);
  }

  /**
  * @brief 展示人脸区域三角剖分线
  * @param show 是否展示
  */
  showMakeupLine(show: boolean) {
    this.queenEngine.showMakeupLine(show);
  }

  /**
   * 引擎注销
   */
  engineDestory() {
    this.queenEngine.engineDestory();
  }

  /**
   * 开启Ar写字
   * @param enable true： 开启
   * @param mode 1: 写字, 2: 画画。
   */
  setArWriting(enable: boolean, mode: number) {
    this.queenEngine.setArWriting(enable, mode)
  }

  /**
   * 清除AR写字痕迹
   */
  cleanScreenArWriting() {
    this.queenEngine.cleanScreenArWriting();
  }
}


export enum kQueenBeautyType {
  SkinBuffing = 0,
  FaceBuffing = 1,
  Makeup = 2,
  /** 美型（主要包括瘦脸、瘦下巴、大眼、瘦鼻、美唇等）
   */
  FaceShape = 3,
  /** 基础美颜（美白）
   */
  SkinWhiting = 4,
  /** 滤镜，当设置滤镜后，可以指定一个滤镜图片
   */
  LUT = 5,
  /** 背景处理，实景抠图，使背景虚化
   */
  BackgroundProcess = 6,
  /** 功能类型: 智能美颜
   */
  AutoFilter = 7,
  /** 功能类型: 美体
   */
  BodyShape = 8,
  /** 功能类型: 手势检测
   */
  HandGestureDetect = 9,
  /** 最大值
   */
  Max = 10
}

export enum kQueenAssetType {
  Background = 0,
  Lookups = 1,
  Mackup = 2,
  Sticker = 3
}

/*
=================================================
kQueenBeautyParams 美颜参数
值为float，除基础美颜外需要先将功能打开，对应参数才有效                   
=================================================
*/
export enum kQueenBeautyParams {
  /** 基础美颜参数项，基础美颜默认打开，不需要手动调用打开接口
  磨皮，值的范围[0,1] ，默认0
  */
  SkinBuffing = 1,
  /** 基础美颜参数项，基础美颜默认打开，不需要手动调用打开接口
   锐化，值的范围[0,1] ，默认0
  */
  Sharpen = 2,
  /** 基础美颜参数项，基础美颜默认打开，不需要手动调用打开接口
   美白，值的范围[0,1] ，默认0
  */
  Whitening = 3,
  /** 脸部美颜参数项，需要先打开QueenBeautyTypeFaceBuffing 项
   去眼袋，值的范围[0,1]，默认0
  */
  Pouch = 4,
  /** 脸部美颜参数项，需要先打开QueenBeautyTypeFaceBuffing 项
   去法令纹，值的范围[0,1]，默认0
  */
  NasolabialFolds = 5,
  /** 色卡滤镜参数项，需要先打开QueenBeautyTypeLUT 项
   色卡滤镜强度，值的范围[0,1]，默认0
  */
  LUT = 6,
  /** 脸部美颜参数项，需要先打开QueenBeautyTypeFaceBuffing 项
   白牙，值的范围[0,1]，默认0
  */
  WhiteTeeth = 7,
  /** 脸部美颜参数项，需要先打开QueenBeautyTypeFaceBuffing 项
   口红，值的范围[0,1]，默认0
  */
  Lipstick = 8,
  /** 脸部美颜参数项，需要先打开QueenBeautyTypeFaceBuffing 项
   腮红，值的范围[0,1]，默认0
  */
  Blush = 9,
  /** 脸部美颜参数项，需要先打开QueenBeautyTypeFaceBuffing 项
   口红色相 [-0.5,0.5], 默认0
  */
  LipstickColorParam = 10,
  /** 脸部美颜参数项，需要先打开QueenBeautyTypeFaceBuffing 项
   口红饱和度[0,1], 默认0
  */
  LipstickGlossParam = 11,
  /** 脸部美颜参数项，需要先打开QueenBeautyTypeFaceBuffing 项
   口红明度[0,1], 默认0
  */
  LipstickBrightnessParam = 12,
  /** 脸部美颜参数项，需要先打开QueenBeautyTypeFaceBuffing 项
   亮眼[0,1], 默认0
  */
  BrightenEye = 13,
  /** 脸部美颜参数项，需要先打开QueenBeautyTypeFaceBuffing 项
   红润[0,1], 默认0
  */
  SkinRed = 14,
  /** 脸部美颜参数项，需要先打开QueenBeautyTypeFaceBuffing 项
   祛皱纹[0,1], 默认0
  */
  Wrinkles = 15,
  /** 脸部美颜参数项，需要先打开QueenBeautyTypeFaceBuffing 项
   祛暗沉[0,1], 默认0
  */
  BrightenFace = 16,
  /** 脸部美颜参数项，需要先打开 QueenBeautyTypeAutoFilter 项
   智能美颜亮度调节比例[-1,1]
  */
  AutoLRate = 17,
  /** 脸部美颜参数项，需要先打开 QueenBeautyTypeAutoFilter 项
   智能美颜色度a调节比例[-1,1]
  */
  AutoARate = 18,
  /** 脸部美颜参数项，需要先打开 QueenBeautyTypeAutoFilter 项
   智能美颜色度b调节比例[-1,1]
  */
  AutoBRate = 19
}

/*
=================================================
kQueenBeautyMakeupType 美妆类型
需要先打开QueenBeautyTypeMakeup 类型                 
=================================================
*/
export enum kQueenBeautyMakeupType {
  /** 整妆
   */
  Whole = 0,
  /** 高光
   */
  Highlight = 1,
  /** 美瞳
   */
  Eyeball = 2,
  /** 口红
   */
  Mouth = 3,
  /** 卧蚕
   */
  Wocan = 4,
  /** 眉毛
   */
  EyeBrow = 5,
  /** 腮红
   */
  Blush = 6,
  /** 眼影
   */
  EyeShadow = 7,
  /** 眼线
   */
  Eyeliner = 8,
  /** 睫毛
   */
  Eyelash = 9,
  /** 最大值
   */
  MakeupMax = 10
}

/*
=================================================
kQueenBeautyFaceShapeType 美型类型
需要先打开kQueenBeautyTypeFaceShape 类型               
=================================================
*/
export enum kQueenBeautyFaceShapeType {
  /** 颧骨，值的范围[0,1]，默认0
   */
  CutCheek = 0,
  /** 削脸，值的范围[0,1]，默认0
   */
  CutFace = 1,
  /** 瘦脸，值的范围[0,1]，默认0
   */
  ThinFace = 2,
  /** 脸长，值的范围[0,1]，默认0
   */
  LongFace = 3,
  /** 下巴缩短，值的范围[-1,1]，默认0
   */
  LowerJaw = 4,
  /** 下巴拉长，值的范围[-1,1]，默认0
   */
  HigherJaw = 5,
  /** 瘦下巴，值的范围[0,1]，默认0
   */
  ThinJaw = 6,
  /** 瘦下颌，值的范围[0,1]，默认0
   */
  ThinMandible = 7,
  /** 大眼，值的范围[0,1]，默认0
   */
  BigEye = 8,
  /** 眼角1，值的范围[0,1]，默认0
   */
  EyeAngle1 = 9,
  /** 眼距，值的范围[-1,1]，默认0
   */
  Canthus = 10,
  /** 拉宽眼距，值的范围[-1,1]，默认0
   */
  Canthus1 = 11,
  /** 眼角2，值的范围[-1,1]，默认0
   */
  EyeAngle2 = 12,
  /** 眼睛高度，值的范围[-1,1]，默认0
   */
  EyeTDAngle = 13,
  /** 瘦鼻，值的范围[0,1]，默认0
   */
  ThinNose = 14,
  /** 鼻翼，值的范围[0,1]，默认0
   */
  Nosewing = 15,
  /** 鼻长，值的范围[-1,1]，默认0
   */
  NasalHeight = 16,
  /** 鼻头长，值的范围[-1,1]，默认0
   */
  NoseTipHeight = 17,
  /** 唇宽，值的范围[-1,1]，默认0
   */
  MouthWidth = 18,
  /** 嘴唇大小，值的范围[-1,1]，默认0
   */
  MouthSize = 19,
  /** 唇高，值的范围[-1,1]，默认0
   */
  MouthHigh = 20,
  /** 人中，值的范围[-1,1]，默认0
   */
  Philtrum = 21,
  /** 发际线，值的范围[-1,1]，默认0
   */
  HairLine = 22,
  /** 嘴角上扬(微笑)，值的范围[0,1]，默认0
   */
  Smile = 23,
  /** 最大值
   */
  MAX = 24
}
/*
=================================================
kQueenBeautyBlend 美妆混合模式            
=================================================
*/
export enum kQueenBeautyBlend {
  /** 正常
       */
  Normal = 0,
  /** 变亮
    */
  Lighten = 1,
  /** 变暗
    */
  Darken = 2,
  /** 正片叠底
    */
  Multiply = 3,
  /** 划分
    */
  Divide = 4,
  /** 平均
    */
  Average = 5,
  /** 线性减淡
    */
  Add = 6,
  /** 减去
    */
  Subtract = 7,
  /** 差值
    */
  Difference = 8,
  /** 反向
    */
  Negation = 9,
  /** 排除
    */
  Exclusion = 10,
  /** 滤色
    */
  Screen = 11,
  /** 叠加
    */
  Overlay = 12,
  /** 柔光
    */
  SoftLight = 13,
  /** 强光
    */
  HardLight = 14,
  /** 颜色减淡
    */
  ColorDodge = 15,
  /** 颜色加深
    */
  ColorBurn = 16,
  /** 线性减淡
    */
  LinearDodge = 17,
  /** 线性加深
    */
  LinearBurn = 18,
  /** 线性光
    */
  LinearLight = 19,
  /** 亮光
    */
  VividLight = 20,
  /** 点光
    */
  PinLight = 21,
  /** 实色混合
    */
  HardMix = 22,
  /** 反射
    */
  Reflect = 23,
  /** 发光
    */
  Glow = 24,
  /** 颗粒
    */
  Phoenix = 25,
  /** 色相
    */
  Hue = 26,
  /** 饱和度
    */
  Saturation = 27,
  /** 明度
    */
  Luminosity = 28,
  /** 颜色
    */
  Color = 29,
  /** 曲线
    */
  Curve = 30,
  /**
    * 视觉融合模式
    */
  LabMix = 31,
  /** 最大值
    */
  Max = 32
}