package test

import (
	"cicv-data-closedloop/pjisuv_msgs"
	"fmt"
	"github.com/bluenviron/goroslib/v2"
	"log"
	"math"
	"sync"
	"testing"
	"time"
)

var shareVars = new(sync.Map)

func UpdateShareVariable(msg *pjisuv_msgs.PerceptionLocalization) {
	shareVars.Store("OutsideWorkshopFlag", true)

	shareVars.Store("VelocityXOfCicvLocation", msg.VelocityX)
	shareVars.Store("VelocityYOfCicvLocation", msg.VelocityY)
}

func TestGoRosLib(t *testing.T) {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println(fmt.Sprintf("Recovered from panic: [%s], [%#v]", Label(), r))
		}
	}()

	rosNode, err := goroslib.NewNode(goroslib.NodeConf{
		Name:          "eyTest",
		MasterAddress: "localhost:11311",
	})
	if err != nil {
		log.Panicln(err, fmt.Sprintf("failed to create rosNode: [%#v]", err), false)
	}

	_, err = goroslib.NewSubscriber(goroslib.SubscriberConf{
		Node:  rosNode,
		Topic: "/tpperception",
		Callback: func(msg *pjisuv_msgs.PerceptionObjects) {
			Rule(shareVars, msg)
		},
	})
	if err != nil {
		log.Panicln(err, fmt.Sprintf("failed to create subscriber: [%#v]", err), false)
	}

	_, err = goroslib.NewSubscriber(goroslib.SubscriberConf{
		Node:  rosNode,
		Topic: "/cicv_location",
		Callback: func(msg *pjisuv_msgs.PerceptionLocalization) {
			UpdateShareVariable(msg)
		},
	})
	if err != nil {
		log.Panicln(err, fmt.Sprintf("failed to create subscriber: [%#v]", err), false)
	}

	select {}
}

// ---------------------------------------------------------------------------------------------------------------------

func Topic() string {
	return "/tpperception"
}

// Label todo 禁止存在下划线_
// MultiPedestrianAhead
// MultiCarAhead
// MultiTruckAhead
// MultiBicycleAhead
// MultiTricycleAhead
// MultiTrafficConeAhead
func Label() string {
	return "MultiTrafficConeAhead"
}

type Record struct {
	StableFrame      int
	LastExistingTime float64
}

var record *Record = nil

// objTypeCheck 目标物类型检测
// × 金龙车:CAR_TYPE=0, TRUCK_TYPE=1, PEDESTRIAN_TYPE=2, CYCLIST_TYPE=3, UNKNOWN_TYPE=4, UNKNOWN_MOVABLE_TYPE=5, UNKNOWN_UNMOVABLE_TYPE=6
// √ 多功能车:UNKNOWN TYPE=O, PEDESTRIAN TYPE=1, CAR TYPE=2, TRUCK TYPE=3, Bicycle TYPE=4, Tricycle TYPE=5, Traffic Cone TYPE=6
func objTypeCheck(obj *pjisuv_msgs.PerceptionObject) bool {
	const targetType uint8 = 4

	return targetType == obj.Type
}

// objPosCheck 判断目标物位置关系
func objPosCheck(obj *pjisuv_msgs.PerceptionObject) bool {
	const laneWidth = 3.5 // m

	return obj.X > 0 && math.Abs(float64(obj.Y)) < laneWidth*1.5
}

// isEgoStationary 判断主车是否“静止”(速度低于阈值)
func isEgoStationary(shareVars *sync.Map) bool {
	const minSpeed = 1 // m/s

	VelocityXOfCicvLocation, _ := shareVars.Load("VelocityXOfCicvLocation") // float64
	VelocityYOfCicvLocation, _ := shareVars.Load("VelocityYOfCicvLocation")
	speed := math.Sqrt(math.Pow(VelocityXOfCicvLocation.(float64), 2) + math.Pow(VelocityYOfCicvLocation.(float64), 2))

	return speed > minSpeed
}

// Rule 检测前方指定目标物数量
func Rule(shareVars *sync.Map, msg *pjisuv_msgs.PerceptionObjects) string {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Recovered from panic:", r)
		}
	}()

	outsideWorkshopFlag, _ := shareVars.Load("OutsideWorkshopFlag")
	if !outsideWorkshopFlag.(bool) || !isEgoStationary(shareVars) {
		return ""
	}

	const Cgcs2000X = 456256.260152
	const Cgcs2000Y = 4397809.886833
	const MinStableFrameCount = 5   // frame
	const MaxTimeBetweenFrame = 0.5 // second
	const minTargetNum = 3

	if len(msg.Objs) >= minTargetNum {

		currTargetNum := 0
		fmt.Println("\n", time.Unix(int64(msg.Header.TimeStamp), int64(msg.Header.TimeStamp*1e9)%1e9).Format(time.StampNano))
		for _, obj := range msg.Objs {
			// 判断目标物是否满足筛选条件
			if objTypeCheck(&obj) && objPosCheck(&obj) {
				fmt.Println(fmt.Sprintf("id: [%d], type: [%d], x/yrel: [%f, %f], x/yabs: [%f, %f], speed: [%f], size: [%f/%f/%f]",
					obj.Id, obj.Type, obj.X, obj.Y, obj.Xabs-Cgcs2000X, obj.Yabs-Cgcs2000Y, obj.Speed, obj.Length, obj.Width, obj.Height))
				currTargetNum++
			}
		}

		if currTargetNum >= minTargetNum {
			if nil == record {
				record = &Record{
					StableFrame:      1,
					LastExistingTime: msg.Header.TimeStamp,
				}
				fmt.Println("---------- create ----------")
			} else {
				newRecord := Record{
					StableFrame:      1,
					LastExistingTime: msg.Header.TimeStamp,
				}

				if newRecord.LastExistingTime-record.LastExistingTime <= MaxTimeBetweenFrame {
					newRecord.StableFrame = record.StableFrame + 1
					if newRecord.StableFrame == MinStableFrameCount {
						record = nil
						fmt.Println("!!!!!!!!!! found !!!!!!!!!!")
						return Label()
					} else {
						fmt.Println("---------- update ----------")
					}
				} else {
					fmt.Println("---------- reset ----------")
				}
				record = &newRecord
			}
		}

	}

	return ""
}