package Core

/*
#cgo CFLAGS: -I../include
#cgo LDFLAGS: -lHCCore -lhcnetsdk -lhpr -lNPQos -lSuperRender
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include "HCNetSDK.h"

extern void fSerialDataCallBackGo(LONG lSerialHandle,LONG lChannel,char *pRecvDataBuffer,DWORD dwBufSize,void *pUser);
void FSerialDataCallBackCGo(LONG lSerialHandle,LONG lChannel,char *pRecvDataBuffer,DWORD dwBufSize,void *pUser){
    fSerialDataCallBackGo(lSerialHandle, lChannel, pRecvDataBuffer, dwBufSize,pUser);
};

// 异常回调函数
extern void fExceptionCallBackGo(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser);
void FExceptionCallBackCgo(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser)
{
    fExceptionCallBackGo(dwType, lUserID, lHandle, pUser);
}
*/
import "C"
import (
	"math"
	"unsafe"
)

// Init 初始化SDK，调用其他SDK函数的前提。
func Init() error {
	if C.NET_DVR_Init() != 1 {
		return LastError("NET_DVR_Init")
	}
	return nil
}

// Cleanup 释放SDK资源，在程序结束之前调用。在调用时不能同时调用其他任何SDK接口。 Init 和 Cleanup 需要配对使用，即程序里面调用多少次 Init ，退出时就需要调用多少次 Cleanup。
func Cleanup() error {
	if C.NET_DVR_Cleanup() != 1 {
		return LastError("NET_DVR_Cleanup")
	}
	return nil
}

func Login(Ip string, Port int, Username string, Password string) (LONG, NET_DVR_DEVICEINFO_V30, error) {
	cIp := C.CString(Ip)
	defer C.free(unsafe.Pointer(cIp))
	cUsername := C.CString(Username)
	defer C.free(unsafe.Pointer(cUsername))
	cPassword := C.CString(Password)
	defer C.free(unsafe.Pointer(cPassword))
	var DeviceInfo C.NET_DVR_DEVICEINFO_V30
	cUserId := C.NET_DVR_Login_V30(cIp, C.WORD(Port), cUsername, cPassword, &DeviceInfo)
	if LONG(cUserId) == -1 {
		return -1, NET_DVR_DEVICEINFO_V30{}, LastError("NET_DVR_Login_V30")
	}
	DATA := CNET_DVR_DEVICEINFO_V30ToGO(&DeviceInfo)
	return LONG(cUserId), DATA, nil
}

func Logout(userId LONG) error {
	if userId > -1 {
		cResult := C.NET_DVR_Logout(C.LONG(userId))
		if cResult != 1 {
			return LastError("NET_DVR_Logout")
		}
	}
	return nil
}

func SerialStart(UserId LONG, CallBack serialDataCallBack) (LONG, error) {
	SerialStartParam := C.NET_DVR_SERIALSTART_V40{}
	SerialStartParam.dwSize = C.DWORD(unsafe.Sizeof(SerialStartParam))
	SerialStartParam.dwSerialType = C.DWORD(2) //1:232串口；2:485串口
	SerialStartParam.bySerialNum = C.BYTE(1)   //串口编号（设备支持多个RS232串口时有效）
	// 填充 byRes 字段
	for i := 0; i < 255; i++ {
		SerialStartParam.byRes[i] = C.BYTE(0) // 示例值，逐个赋值
	}
	SerialStartHandle := C.NET_DVR_SerialStart_V40(C.LONG(UserId), unsafe.Pointer(&SerialStartParam), C.LONG(unsafe.Sizeof(SerialStartParam)), C.fSerialDataCallBack(C.FSerialDataCallBackCGo), nil)
	if LONG(SerialStartHandle) == -1 {
		return -1, LastError("NET_DVR_SerialStart_V40")
	}
	serialCallBackHooks[LONG(SerialStartHandle)] = CallBack
	return LONG(SerialStartHandle), nil
}

func SerialSend(lSerialHandle LONG, hexbyte []byte) error {
	if C.NET_DVR_SerialSend(C.LONG(lSerialHandle), 1, (*C.char)(unsafe.Pointer(&hexbyte[0])), C.DWORD(len(hexbyte))) == C.FALSE {
		return LastError("NET_DVR_SerialSend")
	}
	return nil
}

func SerialStop(lSerialHandle LONG) error {
	if int(C.NET_DVR_SerialStop(C.LONG(lSerialHandle))) != 1 {
		return LastError("SerialStop")
	}
	delete(serialCallBackHooks, lSerialHandle)
	return nil
}

type CDVR_PTZPOS C.NET_DVR_PTZPOS

func (d *CDVR_PTZPOS) Go() DVR_PTZPOS {
	return DVR_PTZPOS{
		WAction:  1,
		WPanPos:  HEX2DEC(WORD(d.wPanPos)),
		WTiltPos: HEX2DEC(WORD(d.wTiltPos)),
		WZoomPos: HEX2DEC(WORD(d.wZoomPos)),
	}
}
func (d *CDVR_PTZPOS) Set(Action, PanPos, TiltPos, ZoomPos float64) {
	//a1 := DEC2HEX(Action)
	//a2 := DEC2HEX(PanPos)
	//a3 := DEC2HEX(TiltPos)
	//a4 := DEC2HEX(ZoomPos)
	//println(a1, a2, a3, a4)
	d.wAction = C.WORD(Action)
	d.wPanPos = C.WORD(DEC2HEX(PanPos))
	d.wTiltPos = C.WORD(DEC2HEX(TiltPos))
	d.wZoomPos = C.WORD(DEC2HEX(ZoomPos))
	//d.wAction = C.WORD(1)
	//d.wPanPos = C.WORD(0x77)
	//d.wTiltPos = C.WORD(0x88)
	//d.wZoomPos = C.WORD(0x99)
}

type DVR_PTZPOS struct {
	WAction  int
	WPanPos  float64
	WTiltPos float64
	WZoomPos float64
}

// DEC2HEX 将十进制浮点数转换为十六进制整数
func DEC2HEX(dec float64) WORD {
	// 提取千位（对应十进制的百位）
	bai := uint16(dec / math.Pow(10, 2))
	dec -= float64(bai) * math.Pow(10, 2)

	// 提取百位（对应十进制的十位）
	shi := uint16(dec / math.Pow(10, 1))
	dec -= float64(shi) * math.Pow(10, 1)

	// 提取十位（对应十进制的个位）
	ge := uint16(dec / math.Pow(10, 0))
	dec -= float64(ge) * math.Pow(10, 0)

	// 提取个位（对应十进制的十分位）
	xiao := uint16(dec * 10)

	// 合成十六进制数
	hex := WORD(bai)<<12 | WORD(shi)<<8 | WORD(ge)<<4 | WORD(xiao)
	return hex
}

// HEX2DEC 将十六进制整数转换为十进制浮点数
func HEX2DEC(hex WORD) float64 {
	// 提取十六进制数的千位（对应十进制的百位）
	bai := byte(hex >> 12)
	hex = hex - WORD(bai)*WORD(math.Pow(16, 3))
	// 提取十六进制数的百位（对应十进制的十位）
	shi := byte(hex >> 8)
	hex = hex - WORD(shi)*WORD(math.Pow(16, 2))
	// 提取十六进制数的十位（对应十进制的个位）
	ge := byte(hex >> 4)
	hex = hex - WORD(ge)*WORD(math.Pow(16, 1))
	// 提取十六进制数的个位（对应十进制的十分位）
	xiao := byte(hex)

	// 计算对应的十进制浮点数
	return float64(bai)*math.Pow(10, 2) + float64(shi)*math.Pow(10, 1) + float64(ge)*math.Pow(10, 0) + float64(xiao)*math.Pow(10, -1)
}

func GetDVRConfig(lUserID LONG, dwCommand DWORD, lChannel LONG, lpOutBuffer unsafe.Pointer, dwOutBufferSize DWORD) error {
	bytesReturned := C.DWORD(0)
	cResult := C.NET_DVR_GetDVRConfig(C.LONG(lUserID), C.DWORD(dwCommand), C.LONG(lChannel), C.LPVOID(lpOutBuffer), C.DWORD(dwOutBufferSize), &bytesReturned)
	if cResult != 1 {
		return LastError("NET_DVR_GetDVRConfig")
	}
	return nil
}
func SetDVRConfig(lUserID LONG, dwCommand DWORD, lChannel LONG, lpInBuffer unsafe.Pointer, dwOutBufferSize DWORD) error {
	cResult := C.NET_DVR_SetDVRConfig(C.LONG(lUserID), C.DWORD(dwCommand), C.LONG(lChannel), C.LPVOID(lpInBuffer), C.DWORD(dwOutBufferSize))
	if cResult != 1 {
		return LastError("NET_DVR_SetDVRConfig")
	}
	return nil
}

func LastError(funcName string) error {
	cCode := C.NET_DVR_GetLastError()
	if cCode == 0 {
		return nil
	}

	var text string
	if cCode == 3 {
		text = "sdk not init."
	} else {
		cCode2 := C.LONG(cCode)
		cText := C.NET_DVR_GetErrorMsg(&cCode2)
		text = C.GoString(cText)
	}
	return NewHcnetError(int(cCode), text, funcName)
}

func PTZControlWithSpeed_Other(lUserID LONG, lChannel LONG, dwPTZCommand DWORD, dwStop DWORD, dwSpeed DWORD) error {
	state := C.NET_DVR_PTZControlWithSpeed_Other(C.LONG(lUserID), C.LONG(lChannel), C.DWORD(dwPTZCommand), C.DWORD(dwStop), C.DWORD(dwSpeed))
	if state == C.FALSE {
		return LastError("NET_DVR_PTZControlWithSpeed_Other")
	}
	return nil
}

func CNET_DVR_DEVICEINFO_V30ToGO(CData *C.NET_DVR_DEVICEINFO_V30) NET_DVR_DEVICEINFO_V30 {
	var goData NET_DVR_DEVICEINFO_V30

	// Copy the SSerialNumber field
	for i := 0; i < 48; i++ {
		goData.SSerialNumber[i] = BYTE(byte(CData.sSerialNumber[i]))
	}

	// Copy other fields
	goData.ByAlarmInPortNum = BYTE(CData.byAlarmInPortNum)
	goData.ByAlarmOutPortNum = BYTE(CData.byAlarmOutPortNum)
	goData.ByDiskNum = BYTE(CData.byDiskNum)
	goData.ByDVRType = BYTE(CData.byDVRType)
	goData.ByChanNum = BYTE(CData.byChanNum)
	goData.ByStartChan = BYTE(CData.byStartChan)
	goData.ByAudioChanNum = BYTE(CData.byAudioChanNum)
	goData.ByIPChanNum = BYTE(CData.byIPChanNum)
	goData.ByZeroChanNum = BYTE(CData.byZeroChanNum)
	goData.ByMainProto = BYTE(CData.byMainProto)
	goData.BySubProto = BYTE(CData.bySubProto)
	goData.BySupport = BYTE(CData.bySupport)
	goData.BySupport1 = BYTE(CData.bySupport1)
	goData.BySupport2 = BYTE(CData.bySupport2)
	goData.WDevType = WORD(CData.wDevType)
	goData.BySupport3 = BYTE(CData.bySupport3)
	goData.ByMultiStreamProto = BYTE(CData.byMultiStreamProto)
	goData.ByStartDChan = BYTE(CData.byStartDChan)
	goData.ByStartDTalkChan = BYTE(CData.byStartDTalkChan)
	goData.ByHighDChanNum = BYTE(CData.byHighDChanNum)
	goData.BySupport4 = BYTE(CData.bySupport4)
	goData.ByLanguageType = BYTE(CData.byLanguageType)
	goData.ByVoiceInChanNum = BYTE(CData.byVoiceInChanNum)
	goData.ByStartVoiceInChanNo = BYTE(CData.byStartVoiceInChanNo)

	return goData
}
