package main

import (
	"context"
	"time"

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

type EventActuator struct {
	parent *LcDevice
	ctx    context.Context
	cancel context.CancelFunc
}

func NewEventActuator(parent *LcDevice) *EventActuator {
	ctx, cancel := context.WithCancel(context.Background())
	ea := EventActuator{
		parent: parent,
		ctx:    ctx,
		cancel: cancel,
	}
	return &ea
}

func (o *EventActuator) Cancel() {
	o.cancel()
}

func (o *EventActuator) EventHandle(XAddr string) {
	defer func() {
		recover()
		if o.parent.EventActuator != nil {
			o.parent.EventActuator.Cancel()
			o.parent.EventActuator = nil
		}
	}()
Retry:
	//onvif事件
	client := soap.NewClient(soap.WithTimeout(time.Second * 0))
	client.AddHeader(soap.NewWSSSecurityHeader(o.parent.onvifDev.User, o.parent.onvifDev.Password))
	epy := event.NewEventPortType(client, XAddr)
	resp0, err0 := epy.GetServiceCapabilities(&event.GetServiceCapabilities{})
	if err0 != nil {
		return
	}
	if !resp0.Capabilities.WSPullPointSupport {
		return
	}
	resp1, err1 := epy.GetEventProperties(&event.GetEventProperties{})
	if err1 != nil {
		return
	}

	//订阅在onvif.json中Event配置的事件主题
	var TopicExpressionDialect event.AnyURI = "https://www.onvif.org/ver10/tev/topicExpression/ConcreteSet"
	if len(resp1.TopicExpressionDialect) > 0 && len(resp1.TopicExpressionDialect[0]) > 0 {
		TopicExpressionDialect = event.AnyURI(resp1.TopicExpressionDialect[0])
	}
	cps := event.CreatePullPointSubscription{
		Filter: event.FilterType{
			TopicExpression: event.TopicExpressionType{
				TopicKinds: o.parent.onvifDev.Event,
				Dialect:    TopicExpressionDialect,
			},
		},
	}
	resp2, err2 := epy.CreatePullPointSubscription(&cps)
	if err2 != nil {
		return
	}

	pps := event.NewPullPointSubscription(client, string(resp2.SubscriptionReference.Address))
	pm := event.PullMessages{MessageLimit: resp0.Capabilities.MaxPullPoints}
	errCnt := 0
	for {
		select {
		case <-o.ctx.Done():
			return
		default:
			resp, err := pps.PullMessages(&pm)
			if err != nil {
				//发生错误，则停3秒再重试
				time.Sleep(3 * time.Second)
				if errCnt++; errCnt > 5 {
					goto Retry
				}
				break
			} else {
				errCnt = 0
			}
			if resp == nil {
				break
			}
			for _, v := range resp.NotificationMessage {
				//一键告警按键告警,v.Topic.TopicKinds == "tns1:Device/Trigger/tnshik:AlarmIn"
				t, err := time.Parse("2006-01-02T15:04:05Z", v.Message.Message.UtcTime)
				if err != nil {
					continue
				}
				ca := protocol.OnvifAlarmInfo{
					AlarmTopic: v.Topic.TopicKinds,
					Time:       protocol.ToBJTime(t).Format("2006-01-02 15:04:05"),
				}
				if len(v.Message.Message.Source.SimpleItem) > 0 {
					ca.Source = make(map[string]string)
					for _, si := range v.Message.Message.Source.SimpleItem {
						ca.Source[si.Name] = si.Value
					}
				}
				if len(v.Message.Message.Key.SimpleItem) > 0 {
					ca.Key = make(map[string]string)
					for _, si := range v.Message.Message.Key.SimpleItem {
						ca.Key[si.Name] = si.Value
					}
				}
				if len(v.Message.Message.Data.SimpleItem) > 0 {
					ca.Data = make(map[string]string)
					for _, si := range v.Message.Message.Data.SimpleItem {
						ca.Data[si.Name] = si.Value
					}
				}
				seq := GetNextUint64()
				var obj protocol.Pack_OnvifAlarm
				if str, err := obj.EnCode(o.parent.onvifDev.Code, appConfig.GID, seq, &ca); err == nil {
					topic := GetTopic(o.parent.GetDevType(), o.parent.onvifDev.Code, protocol.TP_ONVIF_ALARM)
					GetMQTTMgr().Publish(topic, str, mqtt.AtMostOnce, ToAll)
				}
				switch v.Topic.TopicKinds {
				case "tns1:Device/Trigger/tnshik:AlarmIn": //海康一键报警
					o.HandleDeviceTriggerAlarm(seq, &ca) //录像抓图
				case "tns1:RuleEngine/LineDetector/Crossed": //遮挡告警
					o.HandleRuleEngineCrossedLinedetector(seq, &ca)
				}
			}
		}
	}
}

func (o *EventActuator) HandleRuleEngineCrossedLinedetector(seq uint64, ca *protocol.OnvifAlarmInfo) {
	go o.parent.Snapshot("")
}

func (o *EventActuator) HandleDeviceTriggerAlarm(seq uint64, ca *protocol.OnvifAlarmInfo) {
	if v, ok := ca.Data["State"]; ok && v == "true" {
		go o.parent.RecordToFLV("", 20) //告警开始，抓拍视频
	}
	go o.parent.Snapshot("") //告警开始或结束，抓拍图片
}
