import { Injectable } from '@angular/core';
import {
  Platform,
  ToastController,
} from "@ionic/angular"
// import * as faceapi from "face-api.js"
// import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';

import * as tf from '@tensorflow/tfjs-core';
import '@tensorflow/tfjs-backend-webgpu';
import '@tensorflow/tfjs-backend-webgl';
import '@tensorflow/tfjs-backend-wasm';
import * as faceapi from '@vladmandic/face-api';

@Injectable({
  providedIn: 'root'
})
export class FaceApiService {

  isLoaded:boolean = false;
  isLoading:boolean = false;
  constructor(
    public toastController: ToastController,
    private platform:Platform,
  ) { }

  async loadImg(url:string):Promise<HTMLImageElement|null>{
    return new Promise(async (resolve,reject)=>{
      let img = document.createElement("img");
      img.crossOrigin = '*';
      img.src = url
      img.onload = async (data)=>{
        try{
          let startDeect = new Date();
          resolve(img)
        }catch(err){
          console.log(err)
          reject(null)
        }
      }
      img.onerror = (err)=>{
        reject(null)
      }
    })
  }

  mathFace(detect1:any,detect2:any){
    
    let faceMatcher = new faceapi.FaceMatcher([detect1])

    let bestMatch:any;
    let label="",distance=1,score=0;
      
    bestMatch = faceMatcher.findBestMatch(detect2.descriptor)
    console.log("bestMatch",bestMatch)
    if(bestMatch?._label.startsWith("person")){
        label = bestMatch._label;
        distance = bestMatch._distance;
    };

    return {distance,score:1-distance,label};
  }

  async getFaceDetections(photo:string,faceimg:HTMLImageElement){
    this.isLoading = false;
    await this.loadFaceModule(true);

    let canvas = faceapi.createCanvasFromMedia(faceimg)
    let referenceImage = await this.loadImg(photo);
    if(!(referenceImage)){
        console.log("图片不够清晰请重新上传")
        return null;
    }

    console.log("referenceImage",referenceImage)
    let detections
    try{
      detections = await faceapi.detectAllFaces(referenceImage)
      .withFaceLandmarks()
      .withFaceDescriptors()
      .withFaceExpressions()
    }catch(err){
      console.error(err)
    }
    console.log("detections",detections)

    if(!detections){
        console.log("图片不够清晰请重新上传")
        return null;
    }
    if(detections.length==0){
        // openapi.goWrong(response,"照片未上传或不清晰，请重新上传");
        let imgstr = ""
        if(detections.length==0){
            imgstr += "<1>"
        }
        console.log(`图${imgstr}无法识别人脸`)
        return null;
    }

    faceapi.draw.drawDetections(canvas,detections)
    faceapi.draw.drawFaceLandmarks(canvas,detections)
    faceapi.draw.drawFaceExpressions(canvas,detections)
    faceimg.src = canvas.toDataURL()

    // 获取特征向量 68关键点 以及128特征向量
    return detections
  }
  async detectAllFaces(input: faceapi.TNetInput, options?: faceapi.FaceDetectionOptions){
    console.log(input,this.isLoaded)
    if(!this.isLoaded){
      return [
          {_score:0}
      ]
    }
    return await faceapi?.detectAllFaces(input,options)//.withFaceLandmarks(true);
  }
  /**
   * 加载FaceApis模型库
   * @param needAll 是否加载完整模型
   * @returns 
   * @desc
   * 移动端，需要通过FileSystem获取文件本地Uri传入load加载
   * adb shell 进入文件夹进行检索 data/user/0/com.nova.app/files => assets 被集成在apk的二进制包中，无法从文件目录找到
   * file:///android_asset/ 读取目录，逐层寻找assets目录 => 
   * 快速打包测试方法
   * ng build nova-mirror
   * cp -rf dist/nova-mirror/* www/
   * ionic cap run android --project=nova-mirror
   * 暂时：通过HTTPS线上加载模型资源
   * @see
   * 其他替代库：https://docs.regulaforensics.com/develop/face-sdk/mobile/face-detection/basic-detection/
   * 其他替代库：https://docs.regulaforensics.com/develop/face-sdk/mobile/installation/ionic/
   */
  async loadFaceModule(needAll=false){
    await this.setBackend()
    let prefix = ""
    if(this.platform.is("capacitor")) prefix = "https://pwa.fmode.cn/"
    if(this.isLoading) return;
    this.isLoading = true
    // 仅加载面部识别Model库
    await faceapi.nets.ssdMobilenetv1.loadFromUri(prefix+"assets/face/models")
    if(needAll){
      await faceapi.nets.faceRecognitionNet.loadFromUri(prefix+"assets/face/models")
      // await faceapi.nets.tinyFaceDetector.loadFromUri(prefix+"assets/face/models")
      await faceapi.nets.faceLandmark68Net.loadFromUri(prefix+"assets/face/models")
      await faceapi.nets.faceExpressionNet.loadFromUri(prefix+"assets/face/models")
      // await faceapi.nets.faceLandmark68TinyNet.loadFromUri(prefix+"assets/face/models")
    }
    this.isLoaded = true
    // this.toast("DETECT"+ new Date(),)
    return
  }
  async setBackend(){

        let backend
        let WebGPU = (navigator as any).gpu
        if (WebGPU) {
          // WebGPU is supported
          // console.log(WebGPU)
          backend = "webgpu"
        } else {
          // WebGPU is not supported
        }
        let glcanvas = document.createElement('canvas');
        let WebGL = glcanvas.getContext('webgl') || glcanvas.getContext('experimental-webgl');
        if (WebGL) {
          // console.log(WebGL)
          // WebGL is supported
          if(!backend) backend = "webgl"
        } else {
          // WebGL is not supported
        }

        if (typeof WebAssembly === 'object' && typeof WebAssembly.instantiate === 'function') {
          // WebAssembly is supported
          // console.log(WebAssembly)
          if(!backend) backend = "wasm"
        } else {
          // WebAssembly is not supported
        }

        backend&&await tf.setBackend(backend);
        await tf.ready();
        return
  }
  async toast(msg:any){
    let toast = await this.toastController.create({
      message: String(msg),
      duration: 1000,
    })
    toast.present()
  }
}
