package vqdcms

/*
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
*/
import "C"
import (
	"context"
	"easyvqd/internal/core/host"
	"easyvqd/pkg/decoder"
	"fmt"
	"log/slog"
	"os"
	"path/filepath"
	"runtime/debug"
	"strings"
	"sync"
	"sync/atomic"
	"time"
	"unsafe"
)

const MAX_STREAM_CHAN_NUM = 256

type VideoInfoVQD struct {
	mu              sync.RWMutex
	VQDHandle       uintptr
	Params          VQDPara
	IsCreateSuccess bool
}
type VQDHandleInfo struct {
	TaskID       int
	Plans        string
	TaskName     string
	ChannelID    string
	ChannelName  string
	PlanID       int
	PlanName     string
	TemplateID   int
	TemplateName string
}
type VQDHandle struct {
	running    uint32
	ID         int // 标识ID
	info       VQDHandleInfo
	plansLock  sync.RWMutex
	playTicker *Scheduler
	ErrorMsg   string
	data       chan ChanData
	dataLock   sync.RWMutex
	cb         VQDResultCB
	handle     *VideoInfoVQD
	name       string // 算法名称
	decoder    *decoder.VideoDecoder
	fileLock   sync.RWMutex
	hostCore   *host.Core
	Status     VqdTaskStatus
}

type VQDResultCB func(AbnormalModel)

type ChanData struct {
	data []byte
	w    int
	h    int
	now  time.Time
}

// IsCurTimeInRecordPlan 根据一周每天每小时的开关状态判断当前时间是否为开启状态
func IsCurTimeInRecordPlan(schedule string, now time.Time) bool {
	if len(schedule) != 7*24 {
		slog.Error("schedule length is not 7*24", "schedule", schedule)
		return false
	}
	dayOfWeek := int(now.Weekday()+6) % 7 // 调整为0-6，对应周一至周日
	hourOfDay := now.Hour()

	// 计算位置索引
	index := (dayOfWeek * 24) + hourOfDay

	// 检查索引位置的字符是否为'1'
	if index >= 0 && index < len(schedule) && schedule[index] == '1' {
		return true
	}
	return false
}

func NewVQDHandle(cb VQDResultCB, hostCore *host.Core, info VQDHandleInfo) *VQDHandle {
	v := &VQDHandle{
		running:    0,
		decoder:    &decoder.VideoDecoder{},
		info:       info,
		ID:         info.TaskID,
		data:       make(chan ChanData, MAX_STREAM_CHAN_NUM),
		cb:         cb,
		handle:     &VideoInfoVQD{},
		playTicker: NewScheduler(),
		hostCore:   hostCore,
		Status:     TaskStatusStopped,
	}
	err := v.decoder.Create()
	if err != nil {
		slog.Error("decoder Create ", "taskId", info.ChannelID, "err", err)
	}
	v.StartPlay()
	return v
}

func (v *VQDHandle) SetVQDConfig(params VQDPara, info VQDHandleInfo) error {
	v.plansLock.Lock()
	v.info = info
	v.plansLock.Unlock()
	return v.handle.Config(params, params.EnableFunc, 10)
}

func (v *VQDHandle) GetHandle() *VideoInfoVQD {
	return v.handle
}
func (v *VQDHandle) StartPlay() {
	v.Play()
	v.playTicker.Start(25*time.Second, func() {
		v.Play()
	})
}
func (v *VQDHandle) Play() {
	if IsCurTimeInRecordPlan(v.info.Plans, time.Now()) {
		//slog.Info("vqd cms play", "taskId", v.TaskID, "chnId", v.ChnID)
		_, errs := v.hostCore.Play(context.TODO(), &host.PlayInput{
			ChannelID:    v.info.ChannelID,
			ActiveSecond: 0,
		})
		if errs != nil {
			slog.Debug("vqd cms play", "taskId", v.info.TaskID, "chnId", v.info.ChannelID, "err", errs)
			v.ErrorMsg = errs.Error()
			v.Status = TaskStatusFailed
		} else {
			v.ErrorMsg = ""
			v.Status = TaskStatusRunning
		}
	} else {
		v.Status = TaskStatusStopped
	}
}
func (v *VQDHandle) Create(params VQDPara, plan string) *VQDHandle {
	v.plansLock.Lock()
	v.info.Plans = plan
	v.plansLock.Unlock()
	if atomic.LoadUint32(&v.running) == 1 {
		return v
	}
	err := v.handle.Create(params, params.EnableFunc, 10)
	if err != nil {
		slog.Error("vqd create", "taskId", v.info.TaskID, "chnId", v.info.ChannelID, "fail", err)
		return v
	}
	atomic.StoreUint32(&v.running, 1)
	go v.RunFrame()
	return v
}

func (v *VQDHandle) Destroy() {
	if v.data != nil {
		close(v.data)
	}
	if v.decoder != nil {
		err := v.decoder.Destroy()
		if err != nil {
			slog.Error("vqd decoder destroy", "taskId", v.info.TaskID, "chnId", v.info.ChannelID, "err", err)
		}
	}
	v.decoder = nil
	v.playTicker.Stop()
	v.data = nil
	if atomic.LoadUint32(&v.running) == 1 {
		atomic.StoreUint32(&v.running, 0)
		v.handle.Destroy()
	}
}

func (v *VQDHandle) RunFrame() {
	defer func() {
		if e := recover(); e != nil {
			print(fmt.Sprintf("RunFrame---%s\n", e))
			print(fmt.Sprintf("%s\n", string(debug.Stack())))
		}
	}()
	cvqdImgsDir := filepath.Join(CWD(), VQD_IMAGES_DIR, fmt.Sprintf("%d", v.info.TaskID))
	cvqdImgsDir = filepath.ToSlash(cvqdImgsDir)
	if err := os.MkdirAll(cvqdImgsDir, os.ModePerm); err != nil {
		slog.Error("vqd create img dir", "taskId", v.info.TaskID, "chnId", v.info.ChannelID, "err", err)
	}

	hyper := 0
	index := 0
	cdata := ChanData{}
	for {
		select {
		case data, ok := <-v.data:
			if !ok {
				return
			}
			if len(v.data) >= (MAX_STREAM_CHAN_NUM - 6) {
				slog.Error("vqd channel num", "taskId", v.info.TaskID, "chnId", v.info.ChannelID, ">= ", MAX_STREAM_CHAN_NUM)
				hyper = MAX_STREAM_CHAN_NUM / 10
			}
			if hyper > 0 {
				hyper--
				break
			}
			cdata = data

			now := time.Now().UnixMilli()
			fpath := filepath.Join(cvqdImgsDir, fmt.Sprintf("%s_%d_%d_%d.jpg", v.info.ChannelID, v.info.TemplateID, v.info.PlanID, now))
			fpath = filepath.ToSlash(fpath)
			result := VQDResult{}
			ret := v.handle.Frame(cdata.data, cdata.w, cdata.h, index, fpath, &result)
			if ret == 0 {
				index = 0
				if value, b := v.parseVQD(result); b {
					value.FilePath = strings.TrimPrefix(filepath.ToSlash(fpath), filepath.ToSlash(CWD()))
					value.TaskName = v.info.TaskName
					value.ID = v.info.TaskID
					value.ChannelID = v.info.ChannelID
					value.ChannelName = v.info.ChannelName
					value.PlanID = v.info.PlanID
					value.PlanName = v.info.PlanName
					value.TemplateID = v.info.TemplateID
					value.TemplateName = v.info.TemplateName

					if v.cb != nil {
						v.cb(value)
					} else {
						// 保存数据库
						//CreateAbnormalModel(&value)
					}
				}
			}
			//C.free(fp)
			//C.free(yuvBuf)
			index++
		}
	}
}

func (v *VQDHandle) parseVQD(result VQDResult) (AbnormalModel, bool) {
	isabnormal := false
	abnormals := AbnormalModel{
		IsDeep:    v.handle.Params.UseDeepLearning,
		Abnormals: make([]Abnormal, 0),
	}

	if (result.AbnormalType & NXU_VQD_ABN_COLORDEV) != 0 {
		isabnormal = true
		abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
			Value: result.ColorDev,
			Name:  ALNORMAL_NAMES[NXU_VQD_ABN_COLORDEV],
		})
	}
	abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
		Thr1:  v.handle.Params.ColorPara.ColorThr,
		Name1: "偏色阈值",
		Ratio: v.handle.Params.ColorPara.ColorAbnNumRatio,
	})

	if (result.AbnormalType & NXU_VQD_ABN_LIGHT) != 0 {
		isabnormal = true
		abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
			Value: result.LgtDark,
			Name:  ALNORMAL_NAMES[NXU_VQD_ABN_LIGHT],
		})
	}

	if (result.AbnormalType & NXU_VQD_ABN_DARK) != 0 {
		isabnormal = true
		abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
			Value: result.LgtDark,
			Name:  ALNORMAL_NAMES[NXU_VQD_ABN_DARK],
		})
	}
	abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
		Thr1:  v.handle.Params.LgtDarkPara.LightThr,
		Name1: "过亮阈值",
		Thr2:  v.handle.Params.LgtDarkPara.DarkThr,
		Name2: "过暗阈值",
		Ratio: v.handle.Params.LgtDarkPara.LgtDarkAbnNumRatio,
	})

	if (result.AbnormalType & NXU_VQD_ABN_CLARITY) != 0 {
		isabnormal = true
		abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
			Value: result.Clarity,
			Name:  ALNORMAL_NAMES[NXU_VQD_ABN_CLARITY],
		})
	}
	abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
		Thr1:  v.handle.Params.ClarityPara.ClarityThr,
		Name1: "清晰度阈值",
		Ratio: v.handle.Params.ClarityPara.ClarityAbnNumRatio,
	})

	if (result.AbnormalType & NXU_VQD_ABN_NOISE) != 0 {
		isabnormal = true
		abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
			Value: result.Noise,
			Name:  ALNORMAL_NAMES[NXU_VQD_ABN_NOISE],
		})
	}
	abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
		Thr1:  v.handle.Params.NoisePara.NoiseThr,
		Name1: "噪声阈值",
		Ratio: v.handle.Params.NoisePara.NoiseAbnNumRatio,
	})

	if (result.AbnormalType & NXU_VQD_ABN_CONTRAST) != 0 {
		isabnormal = true
		abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
			Value: result.Contrast,
			Name:  ALNORMAL_NAMES[NXU_VQD_ABN_CONTRAST],
		})
	}
	abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
		Thr1:  v.handle.Params.ContrastPara.CtraLowThr,
		Name1: "低对比度阈值",
		Thr2:  v.handle.Params.ContrastPara.CtraHighThr,
		Name2: "高对比度阈值",
		Ratio: v.handle.Params.ContrastPara.CtraAbnNumRatio,
	})

	if (result.AbnormalType & NXU_VQD_ABN_OCCLUSION) != 0 {
		isabnormal = true
		abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
			Value: result.Occlusion,
			Name:  ALNORMAL_NAMES[NXU_VQD_ABN_OCCLUSION],
		})
	}
	abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
		Thr1:  v.handle.Params.OcclusionPara.OcclusionThr,
		Name1: "遮挡阈值",
		Ratio: v.handle.Params.OcclusionPara.OcclusionAbnNumRatio,
	})

	if (result.AbnormalType & NXU_VQD_ABN_BLUE) != 0 {
		isabnormal = true
		abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
			Value: result.Blue,
			Name:  ALNORMAL_NAMES[NXU_VQD_ABN_BLUE],
		})
	}
	abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
		Thr1:  v.handle.Params.BluePara.BlueThr,
		Name1: "蓝屏阈值",
		Ratio: v.handle.Params.BluePara.BlueAbnNumRatio,
	})

	if (result.AbnormalType & NXU_VQD_ABN_SHARK) != 0 {
		isabnormal = true
		abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
			Value: result.Shark,
			Name:  ALNORMAL_NAMES[NXU_VQD_ABN_SHARK],
		})
	}
	abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
		Thr1:  v.handle.Params.SharkPara.SharkThr,
		Name1: "抖动阈值",
		Ratio: v.handle.Params.SharkPara.SharkAbnNumRatio,
	})

	if (result.AbnormalType & NXU_VQD_ABN_FREEZE) != 0 {
		isabnormal = true
		abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
			Value: result.Freeze,
			Name:  ALNORMAL_NAMES[NXU_VQD_ABN_FREEZE],
		})
	}
	abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
		Thr1:  v.handle.Params.FreezePara.FreezeThr,
		Name1: "冻结阈值",
		Ratio: v.handle.Params.FreezePara.FreezeAbnNumRatio,
	})

	if (result.AbnormalType & NXU_VQD_ABN_MOSAIC) != 0 {
		isabnormal = true
		abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
			Value: result.Mosaic,
			Name:  ALNORMAL_NAMES[NXU_VQD_ABN_MOSAIC],
		})
	}
	abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
		Thr1:  v.handle.Params.MosaicPara.MosaicThr,
		Name1: "马赛克阈值",
		Ratio: v.handle.Params.MosaicPara.MosaicAbnNumRatio,
	})

	if (result.AbnormalType & NXU_VQD_ABN_FLOWER) != 0 {
		isabnormal = true
		abnormals.Abnormals = append(abnormals.Abnormals, Abnormal{
			Value: result.Flower,
			Name:  ALNORMAL_NAMES[NXU_VQD_ABN_FLOWER],
		})
	}
	abnormals.DefaultValues = append(abnormals.DefaultValues, DefaultValue{
		Thr1:  v.handle.Params.FlowerPara.FlowerThr,
		Name1: "花屏阈值",
		Ratio: v.handle.Params.FlowerPara.FlowerAbnNumRatio,
	})

	return abnormals, isabnormal
}
func (v *VQDHandle) SendData(buf []byte, _codec int) {
	if !IsCurTimeInRecordPlan(v.info.Plans, time.Now()) {
		return
	}
	w, h, data, err := v.decoder.PushDataEx(buf, _codec)
	if err != nil {
		slog.Error("I帧转YUV失败: ", "TaskID", v.info.TaskID, "err", err)
		return
	}
	if len(data) > 0 && v.data != nil {
		dst := make([]byte, len(data))
		copy(dst, data)
		now := time.Now()
		d := ChanData{
			data: dst,
			w:    w,
			h:    h,
			now:  now,
		}
		v.data <- d
	}
}

// IFrameData 封装I帧数据和指针信息
type IFrameData struct {
	Data    []byte         // I帧原始字节数据
	Pointer unsafe.Pointer // 指向数据的原始指针
	Length  int            // 数据长度（字节数）
	IsValid bool           // 指针是否有效
}

// GetIFramePointer 将字节切片转换为原始指针
// 注意：unsafe包的使用会绕过Go的内存安全检查，需谨慎
func GetIFramePointer(data []byte) *IFrameData {
	if len(data) == 0 {
		return &IFrameData{
			IsValid: false,
			Length:  0,
		}
	}

	// 方式1：直接通过unsafe获取切片底层数组的指针（推荐，高效）
	// 切片的底层结构是：指向数组的指针 + 长度 + 容量
	ptr := unsafe.Pointer(&data[0])

	// 方式2：通过reflect获取指针（更直观展示切片结构，可选）
	// sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&data))
	// ptr := unsafe.Pointer(sliceHeader.Data)

	return &IFrameData{
		Data:    data,
		Pointer: ptr,
		Length:  len(data),
		IsValid: true,
	}
}
func CWD() string {
	geTwd, err := os.Getwd()
	if err != nil {
		return ""
	}
	return geTwd
}
