package HikSDK

/*
#cgo LDFLAGS: -Wl,--allow-multiple-definition
#include <stdio.h>
#include <string.h>
*/
import "C"
import (
	"encoding/binary"
	"errors"
	"fmt"
	"gitea.com/kunmeng/HikNetSDKPkg/Core"
	"sync"
	"time"
	"unsafe"
)

const (
	Base      = 0
	BuKongQiu = 1
)

type PTZEnumObj struct {
	PTZ_LEFT       int
	PTZ_RIGHT      int
	PTZ_UP         int
	PTZ_DOWN       int
	PTZ_UP_LEFT    int
	PTZ_UP_RIGHT   int
	PTZ_DOWN_LEFT  int
	PTZ_DOWN_RIGHT int
	PTZ_ZOOM_IN    int
	PTZ_ZOOM_OUT   int
	PTZ_Focus_Far  int
	PTZ_Focus_Near int
}

func (receiver *PTZEnumObj) toHikPTZEnum(v int) int {
	switch v {
	case PTZEnum.PTZ_LEFT:
		return HikPTZEnum.PAN_LEFT
	case PTZEnum.PTZ_RIGHT:
		return HikPTZEnum.PAN_RIGHT
	case PTZEnum.PTZ_UP:
		return HikPTZEnum.TILT_UP
	case PTZEnum.PTZ_DOWN:
		return HikPTZEnum.TILT_DOWN
	case PTZEnum.PTZ_ZOOM_IN:
		return HikPTZEnum.ZOOM_IN
	case PTZEnum.PTZ_ZOOM_OUT:
		return HikPTZEnum.ZOOM_OUT
	case PTZEnum.PTZ_Focus_Far:
		return HikPTZEnum.FOCUS_FAR
	case PTZEnum.PTZ_Focus_Near:
		return HikPTZEnum.FOCUS_NEAR
	case PTZEnum.PTZ_UP_LEFT:
		return HikPTZEnum.UP_LEFT
	case PTZEnum.PTZ_UP_RIGHT:
		return HikPTZEnum.UP_RIGHT
	case PTZEnum.PTZ_DOWN_LEFT:
		return HikPTZEnum.DOWN_LEFT
	case PTZEnum.PTZ_DOWN_RIGHT:
		return HikPTZEnum.DOWN_RIGHT

	default:
		return -1
	}
}

var PTZEnum = PTZEnumObj{
	PTZ_LEFT:       1,
	PTZ_RIGHT:      2,
	PTZ_UP:         3,
	PTZ_DOWN:       4,
	PTZ_UP_LEFT:    5,
	PTZ_UP_RIGHT:   6,
	PTZ_DOWN_LEFT:  7,
	PTZ_DOWN_RIGHT: 8,
	PTZ_ZOOM_IN:    9,
	PTZ_ZOOM_OUT:   10,
	PTZ_Focus_Far:  11,
	PTZ_Focus_Near: 12,
}

var HikPTZEnum = struct {
	LIGHT_PWRON         int //接通灯光电源
	WIPER_PWRON         int //接通雨刷开关
	FAN_PWRON           int //接通风扇开关
	HEATER_PWRON        int //接通加热器开关
	AUX_PWRON1          int //接通辅助设备开关
	AUX_PWRON2          int //接通辅助设备开关
	ZOOM_IN             int //焦距变大(倍率变大)
	ZOOM_OUT            int //焦距变小(倍率变小)
	FOCUS_NEAR          int //焦点前调
	FOCUS_FAR           int //焦点后调
	IRIS_OPEN           int //光圈扩大
	IRIS_CLOSE          int //光圈缩小
	TILT_UP             int //云台上仰
	TILT_DOWN           int //云台下俯
	PAN_LEFT            int //云台左转
	PAN_RIGHT           int //云台右转
	UP_LEFT             int //云台上仰和左转
	UP_RIGHT            int //云台上仰和右转
	DOWN_LEFT           int //云台下俯和左转
	DOWN_RIGHT          int //云台下俯和右转
	PAN_AUTO            int //云台左右自动扫描
	TILT_DOWN_ZOOM_IN   int //云台下俯和焦距变大(倍率变大)
	TILT_DOWN_ZOOM_OUT  int //云台下俯和焦距变小(倍率变小)
	PAN_LEFT_ZOOM_IN    int //云台左转和焦距变大(倍率变大)
	PAN_LEFT_ZOOM_OUT   int //云台左转和焦距变小(倍率变小)
	PAN_RIGHT_ZOOM_IN   int //云台右转和焦距变大(倍率变大)
	PAN_RIGHT_ZOOM_OUT  int //云台右转和焦距变小(倍率变小)
	UP_LEFT_ZOOM_IN     int //云台上仰和左转和焦距变大(倍率变大)
	UP_LEFT_ZOOM_OUT    int //云台上仰和左转和焦距变小(倍率变小)
	UP_RIGHT_ZOOM_IN    int //云台上仰和右转和焦距变大(倍率变大)
	UP_RIGHT_ZOOM_OUT   int //云台上仰和右转和焦距变小(倍率变小)
	DOWN_LEFT_ZOOM_IN   int //云台下俯和左转和焦距变大(倍率变大)
	DOWN_LEFT_ZOOM_OUT  int //云台下俯和左转和焦距变小(倍率变小)
	DOWN_RIGHT_ZOOM_IN  int //云台下俯和右转和焦距变大(倍率变大)
	DOWN_RIGHT_ZOOM_OUT int //云台下俯和右转和焦距变小(倍率变小)
	TILT_UP_ZOOM_IN     int //云台上仰和焦距变大(倍率变大)
	TILT_UP_ZOOM_OUT    int //云台上仰和焦距变小(倍率变小)
}{LIGHT_PWRON: 2,
	WIPER_PWRON:         3,
	FAN_PWRON:           4,
	HEATER_PWRON:        5,
	AUX_PWRON1:          6,
	AUX_PWRON2:          7,
	ZOOM_IN:             11,
	ZOOM_OUT:            12,
	FOCUS_NEAR:          13,
	FOCUS_FAR:           14,
	IRIS_OPEN:           15,
	IRIS_CLOSE:          16,
	TILT_UP:             21,
	TILT_DOWN:           22,
	PAN_LEFT:            23,
	PAN_RIGHT:           24,
	UP_LEFT:             25,
	UP_RIGHT:            26,
	DOWN_LEFT:           27,
	DOWN_RIGHT:          28,
	PAN_AUTO:            29,
	TILT_DOWN_ZOOM_IN:   58,
	TILT_DOWN_ZOOM_OUT:  59,
	PAN_LEFT_ZOOM_IN:    60,
	PAN_LEFT_ZOOM_OUT:   61,
	PAN_RIGHT_ZOOM_IN:   62,
	PAN_RIGHT_ZOOM_OUT:  63,
	UP_LEFT_ZOOM_IN:     64,
	UP_LEFT_ZOOM_OUT:    65,
	UP_RIGHT_ZOOM_IN:    66,
	UP_RIGHT_ZOOM_OUT:   67,
	DOWN_LEFT_ZOOM_IN:   68,
	DOWN_LEFT_ZOOM_OUT:  69,
	DOWN_RIGHT_ZOOM_IN:  70,
	DOWN_RIGHT_ZOOM_OUT: 71,
	TILT_UP_ZOOM_IN:     72,
	TILT_UP_ZOOM_OUT:    73,
}

type BallCamera struct {
	userId       Core.LONG
	_type        uint8
	deviceInfo   Core.NET_DVR_DEVICEINFO_V30
	mu           sync.Mutex
	expectedType byte
}

func NewBallCamera(Ip string, Port int, Username, Password string, Type uint8) (*BallCamera, error) {
	UserId, DeviceInfo, err := Core.Login(Ip, Port, Username, Password)
	if err != nil {
		return nil, err
	}
	return &BallCamera{
		userId:     UserId,
		_type:      Type,
		deviceInfo: DeviceInfo,
	}, nil
}

type PTZ struct {
	P float32
	T float32
	Z float32
}

func (this *BallCamera) GetPTZ() (PTZ, error) {
	if this._type == BuKongQiu {
		var data PTZ

		ch := make(chan bool)
		SerialStartHandle, err := Core.SerialStart(this.userId, func(lSerialHandle Core.LONG, lChannel Core.LONG, pRecvDataBuffer []byte, dwBufSize Core.DWORD, pUser unsafe.Pointer) {
			if dwBufSize != 7 {
				ch <- false
				return
			}
			Type := pRecvDataBuffer[3]
			this.mu.Lock()
			expected := this.expectedType
			this.mu.Unlock()
			if Type != expected {
				ch <- false
				return
			}
			switch Type {
			case 0x59:
				data.P = float32(binary.BigEndian.Uint16(pRecvDataBuffer[4:6])) / 100.
			case 0x5B:
				data.T = float32(binary.BigEndian.Uint16(pRecvDataBuffer[4:6])) / 100.
			case 0x5D:
				data.Z = float32(binary.BigEndian.Uint16(pRecvDataBuffer[4:6])) / 100.
			default:
				ch <- false
			}
			ch <- true
		})
		if err != nil {
			return data, err
		}
		defer func() {
			err = Core.SerialStop(SerialStartHandle)
			if err != nil {
				println(err.Error())
			}
			time.Sleep(1 * time.Second)
		}()

		// 获取P值
		this.mu.Lock()
		this.expectedType = 0x59
		this.mu.Unlock()
		if err := this.retrySend(SerialStartHandle, []byte{0xff, 0x01, 0x00, 0x51, 0x00, 0x00, 0x52}, 5, ch); err != nil {
			return data, fmt.Errorf("获取P值失败: %w", err)
		}

		this.mu.Lock()
		this.expectedType = 0x5B
		this.mu.Unlock()
		if err := this.retrySend(SerialStartHandle, []byte{0xff, 0x01, 0x00, 0x53, 0x00, 0x00, 0x54}, 5, ch); err != nil {
			return data, fmt.Errorf("获取T值失败: %w", err)
		}

		this.mu.Lock()
		this.expectedType = 0x5D
		this.mu.Unlock()
		if err := this.retrySend(SerialStartHandle, []byte{0xff, 0x01, 0x00, 0x55, 0x00, 0x00, 0x56}, 5, ch); err != nil {
			return data, fmt.Errorf("获取Z值失败: %w", err)
		}
		return data, nil
	}
	var data Core.CDVR_PTZPOS
	var dataPtr = unsafe.Pointer(&data)
	err := Core.GetDVRConfig(this.userId, 293, 1, dataPtr, Core.DWORD(unsafe.Sizeof(data)))
	if err != nil {
		return PTZ{}, err
	}
	res := data.Go()
	return PTZ{
		P: float32(res.WPanPos),
		T: float32(res.WTiltPos),
		Z: float32(res.WZoomPos),
	}, nil

}

func padding(n int) ([]byte, error) {
	if n < 0 || n > 65535 {
		return []byte{0x00, 0x00}, errors.New("n must be in the range 0-65535")
	}
	return []byte{byte(n >> 8), byte(n & 0xFF)}, nil
}

func (this *BallCamera) PtzGotoPut(Action int, P, T, Z float64) error {
	if this._type == BuKongQiu {
		SerialStartHandle, err := Core.SerialStart(this.userId, func(lSerialHandle Core.LONG, lChannel Core.LONG, pRecvDataBuffer []byte, dwBufSize Core.DWORD, pUser unsafe.Pointer) {
		})
		if err != nil {
			return err
		}
		defer func() {
			err = Core.SerialStop(SerialStartHandle)
			if err != nil {
				println(err.Error())
			}
			time.Sleep(1 * time.Second)
		}()

		PByte, err := padding(int(P * 100))
		if err != nil {
			return err
		}
		TByte, err := padding(int(T * 100))
		if err != nil {
			return err
		}
		ZByte, err := padding(int(Z * 100))
		if err != nil {
			return err
		}
		//println(PByte, TByte, ZByte)
		pBuf := append([]byte{0xff, 0x01, 0x00, 0x4b}, PByte...)
		tBuf := append([]byte{0xff, 0x01, 0x00, 0x4d}, TByte...)
		zBuf := append([]byte{0xff, 0x01, 0x00, 0x4f}, ZByte...)

		pBufv, err := verify(pBuf)
		if err != nil {
			return err
		}
		tBufv, err := verify(tBuf)
		if err != nil {
			return err
		}
		zBufv, err := verify(zBuf)
		if err != nil {
			return err
		}
		pBuf = append(pBuf, pBufv)
		tBuf = append(tBuf, tBufv)
		zBuf = append(zBuf, zBufv)
		switch Action {
		case 1:
			err = Core.SerialSend(SerialStartHandle, pBuf)
			if err != nil {
				return err
			}
			err = Core.SerialSend(SerialStartHandle, tBuf)
			if err != nil {
				return err
			}
			err = Core.SerialSend(SerialStartHandle, zBuf)
			if err != nil {
				return err
			}
			break
		case 2:
			err = Core.SerialSend(SerialStartHandle, pBuf)
			if err != nil {
				return err
			}
			break
		case 3:
			err = Core.SerialSend(SerialStartHandle, tBuf)
			if err != nil {
				return err
			}
			break
		case 4:
			err = Core.SerialSend(SerialStartHandle, zBuf)
			if err != nil {
				return err
			}
			break
		case 5:
			err = Core.SerialSend(SerialStartHandle, pBuf)
			if err != nil {
				return err
			}
			err = Core.SerialSend(SerialStartHandle, tBuf)
			if err != nil {
				return err
			}
			break
		default:
			return errors.New("action error")
		}
		return nil
	}
	var data Core.CDVR_PTZPOS
	data.Set(float64(Action), P, T, Z)
	var dataPtr = unsafe.Pointer(&data)
	err := Core.SetDVRConfig(this.userId, 292, 1, dataPtr, Core.DWORD(unsafe.Sizeof(data)))
	if err != nil {
		return err
	}
	return nil
}

func (this *BallCamera) retrySend(handle Core.LONG, cmd []byte, maxRetries int, ch <-chan bool) error {
	for retry := 0; retry < maxRetries; retry++ {
		if err := Core.SerialSend(handle, cmd); err != nil {
			return err
		}

		select {
		case success := <-ch:
			if success {
				return nil
			}
			if retry == maxRetries-1 {
				return fmt.Errorf("达到最大重试次数 %d", maxRetries)
			}
		case <-time.After(2 * time.Second): // 添加超时机制
			if retry == maxRetries-1 {
				return fmt.Errorf("响应超时，重试 %d 次后失败", maxRetries)
			}
		}
	}
	return nil
}

func (receiver *BallCamera) StartBus(direction int, speed int) error {
	err := Core.PTZControlWithSpeed_Other(receiver.userId, Core.LONG(receiver.deviceInfo.ByStartChan), Core.DWORD(direction), Core.DWORD(0), Core.DWORD(speed))
	if err != nil {
		return err
	}
	return nil
}
func (receiver *BallCamera) StopBus(direction int, speed int) error {
	err := Core.PTZControlWithSpeed_Other(receiver.userId, Core.LONG(receiver.deviceInfo.ByStartChan), Core.DWORD(direction), Core.DWORD(1), Core.DWORD(speed))
	if err != nil {
		return err
	}
	return nil
}

func verify(data []byte) (byte, error) {
	if len(data) < 6 {
		return 0, fmt.Errorf("data too short")
	}
	sum := 0
	for i := 1; i < 6; i++ {
		sum += int(data[i])
	}

	// 取模并转换为16进制
	checksum := sum % 0x100
	return byte(checksum), nil
}

func (receiver *BallCamera) Logout() error {
	//if receiver._type == BuKongQiu {
	//	err := core.SerialStop(receiver.serialStartHandle)
	//	if err != nil {
	//		return err
	//	}
	//}
	err := Core.Logout(receiver.userId)
	if err != nil {
		return err
	}
	return nil
}
