package main

import (
	"errors"
	"strconv"
	"time"

	"lc/common/mqtt"
	"lc/common/onvif/profiles/ptz"
	"lc/common/onvif/soap"
	"lc/common/protocol"
)

var (
	NotSupportPtz = errors.New("不支持云台PTZ")
)

// ContinuousMove direction  0:停止移动;1:向左边;2:向右边;3:向上;4:向下;5:缩小;6:放大
// speed/255.0,speed的范围[0,255]
func (o *LcDevice) ContinuousMove(profileToken string, direction, speed uint8) error {
	XAddr, ok := o.endpoints["ptz"]
	if !ok {
		return NotSupportPtz
	}
	p := ptz.NewPTZ(o.client, XAddr)
	var x float32 = 0.0
	var y float32 = 0.0
	var z float32 = 0.0
	s := float32(speed) / 255
	switch direction {
	case 0: //停止移动，用初始值，全零
	case 1: //向左边移动
		x = -s
	case 2: //向右边移动
		x = s
	case 3: //向上移动
		y = s
	case 4: //向下移动
		y = -s
	case 5: //缩小
		z = -s
	case 6: //放大
		z = s
	}
	_, err := p.ContinuousMove(&ptz.ContinuousMove{
		ProfileToken: ptz.ReferenceToken(profileToken),
		Velocity: ptz.PTZSpeed{
			PanTilt: ptz.Vector2D{
				X: x,
				Y: y,
				//Space: "http://www.onvif.org/ver10/tptz/PanTiltSpaces/GenericSpeedSpace",
			},
			Zoom: ptz.Vector1D{
				X: z,
				//Space: "http://www.onvif.org/ver10/tptz/ZoomSpaces/ZoomGenericSpeedSpace",
			},
		},
	})
	return err
}

// GetPresets 获取所有预置点并上报到平台
func (o *LcDevice) GetPresets(profileToken string, filter bool) ([]protocol.PresetInfo, error) {
	XAddr, ok := o.endpoints["ptz"]
	if !ok {
		return nil, NotSupportPtz
	}
	//新球机需要重新生成签名dsz 20230704
	client := soap.NewClient(soap.WithTimeout(time.Second * 0))
	client.AddHeader(soap.NewWSSSecurityHeader(o.onvifDev.User, o.onvifDev.Password))
	o.client = client
	p := ptz.NewPTZ(o.client, XAddr)
	resp, err := p.GetPresets(&ptz.GetPresets{ProfileToken: ptz.ReferenceToken(profileToken)})
	if err != nil {
		return nil, err
	}
	var Data []protocol.PresetInfo
	for _, v := range resp.Preset {
		//海康某球机默认包含300个预置点，需要过滤默认的预置点
		if filter {
			if v.PTZPosition.PanTilt.X == 0 && v.PTZPosition.PanTilt.Y == 1 && v.PTZPosition.Zoom.X == 0 {
				continue
			}
		}
		pi := protocol.PresetInfo{Token: string(v.Token), Name: string(v.Name),
			X: v.PTZPosition.PanTilt.Y, Y: v.PTZPosition.PanTilt.Y, Z: v.PTZPosition.Zoom.X}
		Data = append(Data, pi)
	}
	return Data, nil
}

// SetPreset 设置预置点
func (o *LcDevice) SetPreset(profileToken, presetToken, presetName string) (string, error) {
	XAddr, ok := o.endpoints["ptz"]
	if !ok {
		return "", NotSupportPtz
	}

	//新球机需要重新生成签名dsz 20230704
	client := soap.NewClient(soap.WithTimeout(time.Second * 0))
	client.AddHeader(soap.NewWSSSecurityHeader(o.onvifDev.User, o.onvifDev.Password))
	o.client = client

	p := ptz.NewPTZ(o.client, XAddr)
	resp, err := p.SetPreset(&ptz.SetPreset{
		ProfileToken: ptz.ReferenceToken(profileToken),
		PresetName:   presetName,
		PresetToken:  ptz.ReferenceToken(presetToken),
	})
	if err != nil {
		if _, ok := err.(*soap.SOAPFault); ok { //可能是token错误
			resp, err = p.SetPreset(&ptz.SetPreset{
				ProfileToken: ptz.ReferenceToken(profileToken),
				PresetName:   presetName,
			})
		}
	}
	if err == nil {
		return string(resp.PresetToken), nil
	}
	return "", err
}

// DeletePreset 删除预置点
func (o *LcDevice) DeletePreset(profileToken, presetToken string) error {
	XAddr, ok := o.endpoints["ptz"]
	if !ok {
		return NotSupportPtz
	}
	//新球机需要重新生成签名dsz 20230704
	client := soap.NewClient(soap.WithTimeout(time.Second * 0))
	client.AddHeader(soap.NewWSSSecurityHeader(o.onvifDev.User, o.onvifDev.Password))
	o.client = client

	p := ptz.NewPTZ(o.client, XAddr)
	_, err := p.RemovePreset(&ptz.RemovePreset{
		ProfileToken: ptz.ReferenceToken(profileToken),
		PresetToken:  ptz.ReferenceToken(presetToken),
	})
	return err
}

// GotoPreset 跳转到指定的预置点
func (o *LcDevice) GotoPreset(profileToken, presetToken string) error {
	XAddr, ok := o.endpoints["ptz"]
	if !ok {
		return NotSupportPtz
	}
	//新球机需要重新生成签名dsz 20230704
	client := soap.NewClient(soap.WithTimeout(time.Second * 0))
	client.AddHeader(soap.NewWSSSecurityHeader(o.onvifDev.User, o.onvifDev.Password))
	o.client = client

	p := ptz.NewPTZ(o.client, XAddr)
	_, err := p.GotoPreset(&ptz.GotoPreset{
		ProfileToken: ptz.ReferenceToken(profileToken),
		PresetToken:  ptz.ReferenceToken(presetToken),
	})
	return err
}

// SetHomePosition 跳转到指定的预置点
func (o *LcDevice) SetHomePosition(profileToken string) error {
	XAddr, ok := o.endpoints["ptz"]
	if !ok {
		return NotSupportPtz
	}
	//新球机需要重新生成签名dsz 20230704
	client := soap.NewClient(soap.WithTimeout(time.Second * 0))
	client.AddHeader(soap.NewWSSSecurityHeader(o.onvifDev.User, o.onvifDev.Password))
	o.client = client
	p := ptz.NewPTZ(o.client, XAddr)
	_, err := p.SetHomePosition(&ptz.SetHomePosition{ProfileToken: ptz.ReferenceToken(profileToken)})
	return err
}

// GotoHomePosition 跳转到指定的预置点
func (o *LcDevice) GotoHomePosition(profileToken string) error {
	XAddr, ok := o.endpoints["ptz"]
	if !ok {
		return NotSupportPtz
	}
	//新球机需要重新生成签名dsz 20230704
	client := soap.NewClient(soap.WithTimeout(time.Second * 0))
	client.AddHeader(soap.NewWSSSecurityHeader(o.onvifDev.User, o.onvifDev.Password))
	o.client = client
	p := ptz.NewPTZ(o.client, XAddr)
	_, err := p.GotoHomePosition(&ptz.GotoHomePosition{ProfileToken: ptz.ReferenceToken(profileToken)})
	return err
}

func (o *LcDevice) HandleTpSPtzComm(m mqtt.Message) {
	//新球机需要重新生成签名dsz 20230704
	client := soap.NewClient(soap.WithTimeout(time.Second * 0))
	client.AddHeader(soap.NewWSSSecurityHeader(o.onvifDev.User, o.onvifDev.Password))
	o.client = client

	var obj protocol.Pack_PTZCommonInfo
	if err := obj.DeCode(m.PayloadString()); err != nil {
		return
	}
	if o.onvifDev.Code != obj.Id {
		return
	}
	profileToken := o.GetProfileToken(obj.Data.ProfileToken)
	//0:获取所有预置点，1:设置预置点，2:删除预置点，3:跳转到预置点，4:设置home点，5:跳转到home点
	switch obj.Data.Flag {
	case 0:
		presets, err := o.GetPresets(profileToken, true)
		var ret protocol.Pack_PresetInfo
		strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, obj.Seq, err, obj.Data.Flag, presets)
		GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_PRESETS_ACK), strRet, mqtt.AtMostOnce, ToCloud)
	case 1:
		token, err := o.SetPreset(profileToken, obj.Data.PresetToken, obj.Data.PresetName)
		var (
			ret     protocol.Pack_IPCSetPresetACK
			file    string
			x, y, z float32 = 0, 0, 0
		)
		if err == nil {
			//预置位设置成功，则抓拍一张图片
			tmpFile := o.onvifDev.Code + "_" + strconv.FormatUint(obj.Seq, 10) + ".jpg"
			err = o.Snapshot2(tmpFile)
			if err == nil {
				file = tmpFile
			}
			//获取x,y,z
			if preset, err2 := o.GetPresets(profileToken, true); err2 == nil {
				for _, pis := range preset {
					if pis.Name == obj.Data.PresetName && pis.Token == token {
						x = pis.X
						y = pis.Y
						z = pis.Z
						break
					}
				}
			}
		}
		strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, token, obj.Data.PresetName, file, x, y, z, obj.Seq, err)
		GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_PRESET_ACK), strRet, mqtt.AtMostOnce, ToAll)
	case 2:
		err := o.DeletePreset(profileToken, obj.Data.PresetToken)
		var ret protocol.Pack_IPCCommonACK
		strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, "", obj.Seq, err)
		GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_PTZ_COMM_ACK), strRet, mqtt.AtMostOnce, ToAll)
	case 3:
		err := o.GotoPreset(profileToken, obj.Data.PresetToken)
		var ret protocol.Pack_IPCCommonACK
		strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, "", obj.Seq, err)
		GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_PTZ_COMM_ACK), strRet, mqtt.AtMostOnce, ToAll)
	case 4:
		err := o.SetHomePosition(profileToken)
		var ret protocol.Pack_IPCCommonACK
		strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, "", obj.Seq, err)
		GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_PTZ_COMM_ACK), strRet, mqtt.AtMostOnce, ToAll)
	case 5:
		err := o.GotoHomePosition(profileToken)
		var ret protocol.Pack_IPCCommonACK
		strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, "", obj.Seq, err)
		GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_PTZ_COMM_ACK), strRet, mqtt.AtMostOnce, ToAll)
	case 6:
		Preset, err := o.GetPresets(profileToken, false)
		var ret protocol.Pack_PresetInfo
		strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, obj.Seq, err, obj.Data.Flag, Preset)
		GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_PRESETS_ACK), strRet, mqtt.AtMostOnce, ToCloud)
	}
}

func (o *LcDevice) HandleTpOnvifPtz(m mqtt.Message) {
	var obj protocol.Pack_PTZMoveInfo
	if err := obj.DeCode(m.PayloadString()); err != nil {
		return
	}
	if o.onvifDev.Code != obj.Id {
		return
	}
	//新球机需要重新生成签名dsz 20230704
	client := soap.NewClient(soap.WithTimeout(time.Second * 0))
	client.AddHeader(soap.NewWSSSecurityHeader(o.onvifDev.User, o.onvifDev.Password))
	o.client = client

	token := o.GetProfileToken(obj.Data.ProfileToken)
	err := o.ContinuousMove(token, obj.Data.Direction, obj.Data.Speed)
	var ret protocol.Pack_IPCCommonACK
	strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, "", obj.Seq, err)
	GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_PTZ_COMM_ACK), strRet, mqtt.AtMostOnce, ToAll)
}
