package main

import (
	"errors"
	"github.com/aler9/gortsplib"
	"github.com/gofiber/fiber/v2"
	"image"
	"log"
	"net/url"
	"time"
)

type SceneCommand struct {
	Scene       string   `json:"scene"`
	PushUrl     string   `json:"pushUrl"`
	CameraUrls  []string `json:"cameraURls"`
	proc        func(int64) error
	areaConfigs []AreaConfig `json:"-"`
}

type SceneData struct {
	State   string   `json:"state"`
	LiveUrl []string `json:"liveUrl"`
}

type Pointer struct {
	Id       string  `json:"id"`
	PercentX float32 `json:"percentX"`
	PercentY float32 `json:"percentY"`
}

type AreaConfig struct {
	AreaId      string    `json:"areaId"`
	PointersNum int       `json:"pointersNum"`
	Pointers    []Pointer `json:"pointers"`
}

type SceneReadyResponse struct {
	Scene     string    `json:"scene"`
	Timestamp int64     `json:"timestamp"`
	SceneData SceneData `json:"sceneData"`
}

var currentScene *SceneCommand
var sceneStopChannel chan bool

func validateSceneCmd(cmd SceneCommand) error {
	switch cmd.Scene {
	case "pullUp", "standJump", "sitUps":
		if len(cmd.CameraUrls) < 1 {
			return errors.New("cameraUrls can not less than 1")
		}
	case "race":
		if len(cmd.CameraUrls) < 2 {
			return errors.New("cameraUrls can not less than 2")
		}
	default:
		return errors.New("unknown scene type:" + cmd.Scene)
	}
	if u, e := url.Parse(cmd.PushUrl); nil != e {
		return errors.New("invalid pushUrl:" + e.Error())
	} else if u.Scheme != "http" && u.Scheme != "https" {
		return errors.New("can not support pushUrl with " + u.Scheme)
	}
	return nil
}

func setScene(c *fiber.Ctx) error {
	var sceneCmd SceneCommand
	if err := c.BodyParser(&sceneCmd); nil != err {
		log.Println("ERROR:", c.Body())
		return failureResponse(c, "-1", err.Error(), 200)
	} else if nil != currentScene {
		return failureResponse(c, "1", "A working scene is started", 200)
	} else if err = validateSceneCmd(sceneCmd); nil != err {
		return failureResponse(c, "2", err.Error(), 200)
	} else {
		// currentScene = &sceneCmd
		log.Println("Starting scene ...")
		log.Println(sceneCmd)
		if e := sceneTaskProc(&sceneCmd); nil != e {
			return failureResponse(c, "3", err.Error(), 200)
		} else {
			return successResponse(c, "Updated successfully.")
		}
	}
}

func setAreaConfig(c *fiber.Ctx) error {
	var cfg []AreaConfig
	if e := c.BodyParser(&cfg); nil != e {
		log.Println("ERROR:", c.Body())
		return failureResponse(c, "-1", e.Error(), 200)
	} else if nil == currentScene {
		return failureResponse(c, "1", "No working scene is started", 200)
	}
	currentScene.areaConfigs = cfg
	return successResponse(c, "Updated successfully.")
}

func getAreaConfig(c *fiber.Ctx) error {
	if nil == currentScene {
		return failureResponse(c, "1", "No working scene is started", 200)
	} else {
		return successResponseData(c, "success", currentScene.areaConfigs)
	}
}

func stopScene(c *fiber.Ctx) error {
	if nil != testStopChannel {
		testStopChannel <- true
	}
	if nil != sceneStopChannel {
		sceneStopChannel <- true
	}
	return successResponse(c, "Updated successfully.")
}

func getStatus(c *fiber.Ctx) error {
	var scene = ""
	var seq int64 = 0
	if nil != currentScene {
		scene = currentScene.Scene
	}
	if nil != currentTest {
		seq = currentTest.SequenceId
	}
	return successResponseEx(c, "success", scene, seq)
}

var capturedImage image.Image

func sceneTaskProc(cmd *SceneCommand) error {
	var f func(int64) error = nil
	switch cmd.Scene {
	case "pullUp":
		f = pullUpTask
	case "standJump":
		f = standJumpTask
	case "sitUps":
		f = sitUpsTask
	case "race":
		f = raceTask
	default:
		return errors.New("unknown scene type:" + cmd.Scene)
	}
	if len(cmd.CameraUrls) < 1 {
		return errors.New("no camera specified")
	}
	var cc *gortsplib.Client
	if c, u, e := setupRtsp(cmd.CameraUrls[0]); nil != e {
		log.Println("setupRtsp failed:", e)
	} else {
		cc = c
		go func() {
			if e := playRtsp(c, u, func(img image.Image) {
				//log.Println("...capturedImage:>", img.Bounds())
				capturedImage = img
			}); nil != e {
				//log.Println("playRtsp failed:>", e)
			}
		}()
	}
	sceneStopChannel = make(chan bool)
	var tk = time.NewTicker(time.Second * 3)
	go func() {
		defer func() {
			tk.Stop()
			close(sceneStopChannel)
			if nil != cc {
				_ = cc.Close()
			}
			sceneStopChannel = nil
		}()
		currentScene = cmd
		currentScene.proc = f
		time.Sleep(time.Second)
		var st = SceneReadyResponse{
			Scene:     cmd.Scene,
			Timestamp: time.Now().Unix(),
			SceneData: SceneData{
				State:   "SCENE READY",
				LiveUrl: cmd.CameraUrls,
			},
		}
		go httpPostEx(cmd.PushUrl, st, 3)
		for {
			select {
			case <-tk.C:
				// log.Println(" ...")
			case <-sceneStopChannel:
				log.Println("Scene Stopping...")
				go pushEvent(cmd.PushUrl, cmd.Scene, 0, "SCENE END", nil)
				currentScene = nil
				return
			}
		}
	}()
	return nil
}

func init() {
	currentScene = nil
	sceneStopChannel = nil
}
