package vqdcms

/*
#cgo CFLAGS:  -w -I${SRCDIR}/include  -fPIC
#cgo CPPFLAGS: -w -I${SRCDIR}/include -fPIC
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "nxu_common.h"
#include "nxu_vqd_api.h"
#include "ez_vqd.h"
#include <windows.h>

void LoadDllDir(char *path, int path_size)
{
	SetDllDirectoryA(path);
}

*/
import "C"
import (
	"fmt"
	"log/slog"
	"os"
	"path/filepath"
	"syscall"
	"unsafe"
)

var (
	vqdDLL    *syscall.DLL
	vqdLoad   *syscall.Proc
	vqdUnLoad *syscall.Proc

	vqdCreate, vqdDestroy, vqdConfig, vqdRun *syscall.Proc
)

func VQDInit() (err error) {
	if vqdDLL != nil {
		return
	}
	cwd, _ := os.Getwd()
	vqdSDKDir := filepath.Join(cwd, "VqdSDK")
	C.LoadDllDir(C.CString(vqdSDKDir), C.int(len(vqdSDKDir)))
	vqdDLL, err = syscall.LoadDLL(filepath.Join(vqdSDKDir, "EasyVQD.dll"))
	if err != nil {
		return
	}
	vqdLoad, err = vqdDLL.FindProc("ez_cvr_vqd_load")
	if err != nil {
		VQDUnInit()
		return
	}
	vqdUnLoad, err = vqdDLL.FindProc("ez_cvr_vqd_unload")
	if err != nil {
		VQDUnInit()
		return
	}
	vqdCreate, err = vqdDLL.FindProc("ez_cvr_vqd_create")
	if err != nil {
		VQDUnInit()
		return
	}
	vqdDestroy, err = vqdDLL.FindProc("ez_cvr_vqd_destroy")
	if err != nil {
		VQDUnInit()
		return
	}
	vqdConfig, err = vqdDLL.FindProc("ez_cvr_vqd_config")
	if err != nil {
		VQDUnInit()
		return
	}
	vqdRun, err = vqdDLL.FindProc("ez_cvr_vqd_run")
	if err != nil {
		VQDUnInit()
		return
	}
	vqdSDKDir = filepath.ToSlash(vqdSDKDir)
	dir, err := syscall.BytePtrFromString(vqdSDKDir)
	if err != nil {
		VQDUnInit()
		return err
	}
	var errmsg [128]C.char
	r1, _, _ := vqdLoad.Call(uintptr(unsafe.Pointer(dir)), uintptr(len(vqdSDKDir)), uintptr(unsafe.Pointer(&errmsg[0])))
	if r1 != 0 {
		VQDUnInit()
		resBuf1 := (*[128]byte)(unsafe.Pointer(&errmsg[0]))[:128:128]
		return fmt.Errorf("%s", resBuf1)
	}
	slog.Info("vqd cms open ok")
	return
}

func VQDUnInit() {
	if vqdUnLoad != nil {
		_, _, err := vqdUnLoad.Call()
		if err != nil {
			slog.Error("vqd cms stop Call err", "err", err)
		}
	}
	vqdUnLoad = nil
	if vqdDLL != nil {
		err := vqdDLL.Release()
		if err != nil {
			slog.Error("vqd cms stop Release err", "err", err)
		}
	}
	slog.Info("vqd cms stop ok")
	vqdDLL = nil
}
func (v *VideoInfoVQD) Create(params VQDPara, enable int, interval int) error {
	v.mu.Lock()
	defer v.mu.Unlock()
	v.Params = params
	var stPara C.NXU_VQD_Para_S
	setParams(&stPara, params, enable)
	ret, _, _ := vqdCreate.Call(uintptr(unsafe.Pointer(&v.VQDHandle)))
	if ret != 0 {
		return fmt.Errorf("vqdCreate fail:%d", ret)
	}
	ret, _, _ = vqdConfig.Call(v.VQDHandle, uintptr(unsafe.Pointer(&stPara)), uintptr(interval))
	if ret != 0 {
		return fmt.Errorf("vqdConfig fail:%d", ret)
	}
	v.IsCreateSuccess = true
	return nil
}
func (v *VideoInfoVQD) Config(params VQDPara, enable int, interval int) error {
	v.mu.Lock()
	defer v.mu.Unlock()
	if !v.IsCreateSuccess {
		return fmt.Errorf("vqd cms is not create")
	}
	v.Params = params
	var stPara C.NXU_VQD_Para_S
	setParams(&stPara, params, enable)
	ret, _, _ := vqdConfig.Call(v.VQDHandle, uintptr(unsafe.Pointer(&stPara)), uintptr(interval))
	if ret != 0 {
		return fmt.Errorf("vqdConfig fail:%d", ret)
	}
	return nil
}

func setParams(stPara *C.NXU_VQD_Para_S, params VQDPara, enable int) {
	if params.UseDeepLearning {
		stPara.bUseDeepLearning = C.NXU_TRUE
	} else {
		stPara.bUseDeepLearning = C.NXU_FALSE
	}

	//enable := NXU_VQD_ENABLE_LGTDARK
	// 设置参数
	// 设置亮度检测
	stPara.s32EnableFunc = C.NXU_S32(enable)
	stPara.stLgtDarkPara.f32DarkThr = C.NXU_FLOAT(params.LgtDarkPara.DarkThr)
	stPara.stLgtDarkPara.f32LightThr = C.NXU_FLOAT(params.LgtDarkPara.LightThr)
	stPara.stLgtDarkPara.f32LgtDarkAbnNumRatio = C.NXU_FLOAT(params.LgtDarkPara.LgtDarkAbnNumRatio)
	stPara.s32VecFrmNum = C.int(params.VecFrmNum)

	//enable |= NXU_VQD_ENABLE_BLUE
	// 设置蓝屏检测
	stPara.stBluePara.f32BlueThr = C.NXU_FLOAT(params.BluePara.BlueThr)
	stPara.stBluePara.f32BlueAbnNumRatio = C.NXU_FLOAT(params.BluePara.BlueAbnNumRatio)

	//enable |= NXU_VQD_ENABLE_CLARITY
	// 设置清晰度检测
	stPara.stClarityPara.f32ClarityThr = C.NXU_FLOAT(params.ClarityPara.ClarityThr)
	stPara.stClarityPara.f32ClarityAbnNumRatio = C.NXU_FLOAT(params.ClarityPara.ClarityAbnNumRatio)

	//enable |= NXU_VQD_ENABLE_SHARK
	// 设置抖动检测
	stPara.stSharkPara.f32SharkThr = C.NXU_FLOAT(params.SharkPara.SharkThr)
	stPara.stSharkPara.f32SharkAbnNumRatio = C.NXU_FLOAT(params.SharkPara.SharkAbnNumRatio)

	//enable |= NXU_VQD_ENABLE_FREEZE
	// 设置冻结检测
	stPara.stFreezePara.f32FreezeThr = C.NXU_FLOAT(params.FreezePara.FreezeThr)
	stPara.stFreezePara.f32FreezeAbnNumRatio = C.NXU_FLOAT(params.FreezePara.FreezeAbnNumRatio)

	//enable |= NXU_VQD_ENABLE_COLOR
	// 设置偏色检测
	stPara.stColorPara.f32ColorThr = C.NXU_FLOAT(params.ColorPara.ColorThr)
	stPara.stColorPara.f32ColorAbnNumRatio = C.NXU_FLOAT(params.ColorPara.ColorAbnNumRatio)

	//enable |= NXU_VQD_ENABLE_OCCLUSION
	// 设置遮挡检测
	stPara.stOcclusionPara.f32OcclusionThr = C.NXU_FLOAT(params.OcclusionPara.OcclusionThr)
	stPara.stOcclusionPara.f32OcclusionAbnNumRatio = C.NXU_FLOAT(params.OcclusionPara.OcclusionAbnNumRatio)

	//enable |= NXU_VQD_ENABLE_NOISE
	// 设置噪声检测参数
	stPara.stNoisePara.f32NoiseThr = C.NXU_FLOAT(params.NoisePara.NoiseThr)
	stPara.stNoisePara.f32NoiseAbnNumRatio = C.NXU_FLOAT(params.NoisePara.NoiseAbnNumRatio)

	//enable |= NXU_VQD_ENABLE_CONTRAST
	// 设置对比度检测
	stPara.stContrastPara.f32CtraLowThr = C.NXU_FLOAT(params.ContrastPara.CtraLowThr)
	stPara.stContrastPara.f32CtraHighThr = C.NXU_FLOAT(params.ContrastPara.CtraHighThr)
	stPara.stContrastPara.f32CtraAbnNumRatio = C.NXU_FLOAT(params.ContrastPara.CtraAbnNumRatio)

	//enable |= NXU_VQD_ENABLE_MOSAIC
	// 设置马赛克检测
	stPara.stMosaicPara.f32MosaicThr = C.NXU_FLOAT(params.MosaicPara.MosaicThr)
	stPara.stMosaicPara.f32MosaicAbnNumRatio = C.NXU_FLOAT(params.MosaicPara.MosaicAbnNumRatio)

	//enable |= NXU_VQD_ENABLE_FLOWER
	// 设置花屏检测
	stPara.stFlowerPara.f32FlowerThr = C.NXU_FLOAT(params.FlowerPara.FlowerThr)
	stPara.stFlowerPara.f32FlowerAbnNumRatio = C.NXU_FLOAT(params.FlowerPara.FlowerAbnNumRatio)
}

func (v *VideoInfoVQD) Frame(data []byte, w, h, frameNum int, save_filename string, outResult *VQDResult) int {
	v.mu.Lock()
	defer v.mu.Unlock()
	if !v.IsCreateSuccess {
		slog.Info("vqd cms is not create")
		return -10
	}
	var result C.NXU_VQD_Result_S
	yuv := C.CBytes(data)
	defer C.free(yuv)
	filename, _ := syscall.BytePtrFromString(save_filename)
	ret, _, _ := vqdRun.Call(v.VQDHandle, uintptr(w), uintptr(h), uintptr(yuv), uintptr(frameNum), uintptr(unsafe.Pointer(filename)), uintptr(len(save_filename)), uintptr(unsafe.Pointer(&result)))
	if ret == 0 {
		// 分析成功
		outResult.AbnormalType = int(result.s32AbnormalType)
		outResult.ColorDev = float32(result.f32ColorDev)
		outResult.LgtDark = float32(result.f32LgtDark)
		outResult.Clarity = float32(result.f32Clarity)
		outResult.Noise = float32(result.f32Noise)
		outResult.Contrast = float32(result.f32Contrast)
		outResult.Occlusion = float32(result.f32Occlusion)
		outResult.Blue = float32(result.f32Blue)
		outResult.Shark = float32(result.f32Shark)
		outResult.Freeze = float32(result.f32Freeze)
		outResult.Mosaic = float32(result.f32Mosaic)
		outResult.Flower = float32(result.f32Flower)
	}
	return int(ret)
}
func (v *VideoInfoVQD) Destroy() {
	v.mu.Lock()
	defer v.mu.Unlock()
	if !v.IsCreateSuccess {
		slog.Error("vqd cms Destroy fail")
		return
	}
	v.IsCreateSuccess = false
	_, _, err := vqdDestroy.Call(uintptr(unsafe.Pointer(&v.VQDHandle)))
	if err != nil {
		slog.Error("vqd cms Destroy Call", err, err)
		return
	}
}
