package lc

import (
	"encoding/xml"
	"fmt"
	"github.com/sirupsen/logrus"
	"io"
	"io/ioutil"
	"lc-smartX/lc/model"
	"lc-smartX/util"
	"lc-smartX/util/gopool"
	"log"
	"mime/multipart"
	"net/http"
	"strings"
	"time"
)

type Notifier interface {
	Notify()
}

func StartEventServer() {
	S = &Server{Cameras: util.Config.Cameras, Notifiers: make(map[string]Notifier, 4)}
	fmt.Println("cameras:", S.Cameras)
	S.start()
}

var S *Server

type Server struct {
	Cameras   []model.CameraInfo
	Notifiers map[string]Notifier
}

func (s *Server) start() {
	http.HandleFunc(util.Config.HikServer.Path, handler)
	logrus.Fatal("事件监听服务启动失败:", util.Config.HikServer.Addr, ", error:", http.ListenAndServe(util.Config.HikServer.Addr, nil))
}

func RegisterCallback(branch byte, notifer Notifier) {
	for _, camera := range S.Cameras {
		//关联主路led屏和支路摄像头;关联支路led屏和主路摄像头
		if branch == 0 && camera.Branch == 1 || branch == 1 && camera.Branch == 0 {
			S.Notifiers[camera.IP] = notifer
		}
	}
}

func (s *Server) Callback(ip string) {
	notifier, ok := s.Notifiers[ip]
	if !ok {
		logrus.Errorf("回调函数注册表没有该ip:%s", ip)
		return
	}
	notifier.Notify()
	logrus.Debugf("camera %s Callback", ip)
}

func handler(w http.ResponseWriter, r *http.Request) {
	gopool.Go(func() {
		//监听主机应答固定,直接先应答
		w.WriteHeader(200)
		w.Header().Add("Date", time.Now().String())
		w.Header().Add("Connection", "keep-alive")
	})
	//1.
	contentType := r.Header.Get("Content-Type")
	if strings.Contains(contentType, "application/xml") {
		bytes, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return
		}
		var event model.EventNotificationAlert
		err = xml.Unmarshal(bytes, &event)
		if err != nil {
			return
		}
		//发送事件通知
		S.Callback(event.IpAddress)
	} else if strings.Contains(contentType, "multipart/form-data") {
		handleMultipart(r)
	}
}

// 处理多文件事件
func handleMultipart(r *http.Request) {
	multipartReader := multipart.NewReader(r.Body, "boundary")
	// 循环读取每个 part
	var event model.EventNotificationAlert
	for {
		part, err := multipartReader.NextPart()
		//defer part.Close()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Println("Failed to read part:", err)
			return
		}
		if part.FormName() != "linedetectionImage" || !strings.Contains(part.FormName(), "Image") {
			//不含图片的xml部分数据
			xmlData, err := ioutil.ReadAll(part)
			if err != nil {
				return
			}
			xml.Unmarshal(xmlData, &event)
			continue
		}
	}
	//发送事件通知
	S.Callback(event.IpAddress)
}
