package controllers

import (
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/astaxie/beego"
	validation "github.com/go-ozzo/ozzo-validation/v4"

	"lc/common/models"
	"lc/common/mqtt"
	"lc/common/protocol"
	"lc/common/util"
)

type CameraController struct {
	BaseController
}

type ReqCamera struct {
	Code    string `json:"code"`    //设备编号，禁止修改
	GID     string `json:"gid"`     //网关ID
	Tenant  string `json:"tenant"`  //租户ID
	Name    string `json:"name"`    //设备名称
	Brand   int    `json:"brand"`   //品牌
	Model   int    `json:"model"`   //型号
	DevType int    `json:"devtype"` //设备类型
	State   int    `json:"state"`   //1启用，0禁用
}

func (a ReqCamera) Validate() error {
	return validation.ValidateStruct(&a,
		validation.Field(&a.Code, validation.Required, validation.Length(4, 32)),
		validation.Field(&a.GID, validation.Required, validation.Length(4, 32)),
		validation.Field(&a.Tenant, validation.Required, validation.Length(4, 8)),
		validation.Field(&a.State, validation.In(0, 1)),
	)
}

type Camera struct {
	Code    string `json:"code"`    //设备编号，禁止修改
	GID     string `json:"gid"`     //网关ID
	Name    string `json:"name"`    //设备名称
	Brand   int    `json:"brand"`   //品牌
	Model   int    `json:"model"`   //型号
	DevType int    `json:"devtype"` //设备类型
	State   int    `json:"state"`   //1启用，0禁用
}

func (a Camera) Validate() error {
	return validation.ValidateStruct(&a,
		validation.Field(&a.Code, validation.Required, validation.Length(4, 32)),
		validation.Field(&a.GID, validation.Required, validation.Length(4, 32)),
		validation.Field(&a.State, validation.In(0, 1)),
	)
}

type ReqImportCamera struct {
	Tenant string   `json:"tenant"` //租户ID
	List   []Camera `json:"list"`   //列表
}

func (a ReqImportCamera) Validate() error {
	var rules []*validation.FieldRules
	rules = append(rules, validation.Field(&a.Tenant, validation.Required, validation.Length(4, 8)))
	rules = append(rules, validation.Field(&a.List, validation.Required))
	return validation.ValidateStruct(&a, rules...)
}

type RespSnapshotUrl struct {
	Code string   `json:"code"`
	Date string   `json:"date"`
	URLs []string `json:"urls"`
}

type PresetInfo struct {
	Token string `json:"token"`
	Name  string `json:"name"`
	File  string `json:"file"`
}
type RespPresetInfo struct {
	Code string       `json:"code"`
	Data []PresetInfo `json:"data"`
}

// CreateCamera @Title 创建摄像头或一键告警设备
// @Description 创建摄像头或一键告警设备
// @Param    body controllers.ReqCamera true "数据"
// @Success 0 {int} BaseResponse.Code "成功"
// @Failure 1 {int} BaseResponse.Code "失败"
// @router /v1/create [post]
func (o *CameraController) CreateCamera() {
	var obj ReqCamera
	if err := json.Unmarshal(o.Ctx.Input.RequestBody, &obj); err != nil {
		beego.Debug(string(o.Ctx.Input.RequestBody))
		o.Response(Failure, fmt.Sprintf("数据包解析错误:%s", err.Error()), nil)
		return
	}
	if err := obj.Validate(); err != nil {
		beego.Error(fmt.Sprintf("请求参数验证失败:%s", err.Error()))
		o.Response(Failure, fmt.Sprintf("请求参数验证失败:%s", err.Error()), nil)
		return
	}
	oo := models.CameraDevice{
		ID:      obj.Code, //设备ID，DID
		Name:    obj.Name, //集控器名称
		GID:     obj.GID,  //网关ID
		DevType: obj.DevType,
		Tenant:  obj.Tenant,
		State:   obj.State, //1启用，0禁用
	}
	if err := oo.SaveFromWeb(); err != nil {
		o.Response(Failure, fmt.Sprintf("数据插入失败:%s", err.Error()), nil)
		return
	}
	o.Response(Success, "成功", oo.ID)
}

// UpdateCamera @Title 更新摄像头或一键告警设备
// @Description 更新摄像头或一键告警设备
// @Param    body controllers.ReqCamera true "数据"
// @Success 0 {int} BaseResponse.Code "成功"
// @Failure 1 {int} BaseResponse.Code "失败"
// @router /v1/update [post]
func (o *CameraController) UpdateCamera() {
	var obj ReqCamera
	if err := json.Unmarshal(o.Ctx.Input.RequestBody, &obj); err != nil {
		beego.Debug(string(o.Ctx.Input.RequestBody))
		o.Response(Failure, fmt.Sprintf("数据包解析错误:%s", err.Error()), nil)
		return
	}
	if err := obj.Validate(); err != nil {
		beego.Error(fmt.Sprintf("请求参数验证失败:%s", err.Error()))
		o.Response(Failure, fmt.Sprintf("请求参数验证失败:%s", err.Error()), nil)
		return
	}
	oo := models.CameraDevice{
		ID:      obj.Code, //设备ID，DID
		Name:    obj.Name, //集控器名称
		GID:     obj.GID,  //网关ID
		DevType: obj.DevType,
		Tenant:  obj.Tenant,
		State:   obj.State, //1启用，0禁用
	}
	if err := oo.SaveFromWeb(); err != nil {
		o.Response(Failure, fmt.Sprintf("数据更新失败:%s", err.Error()), nil)
		return
	}
	o.Response(Success, "成功", oo.ID)
}

// DeleteCamera @Title 删除摄像头或一键告警设备
// @Description 删除摄像头或一键告警设备
// @Param   code    query   string     true "设备ID"
// @Success 0 {int} BaseResponse.Code "成功"
// @Failure 1 {int} BaseResponse.Code "失败"
// @router /v1/delete [post]
func (o *CameraController) DeleteCamera() {
	code := strings.Trim(o.GetString("code"), " ")
	if err := validation.Validate(code, validation.Required, validation.Length(4, 100)); err != nil {
		beego.Error(fmt.Sprintf("请求参数验证失败:%s", err.Error()))
		o.Response(Failure, fmt.Sprintf("请求参数验证失败:%s", err.Error()), nil)
		return
	}
	c := models.CameraDevice{
		ID: code,
	}
	if err := c.Delete(); err != nil {
		beego.Error(fmt.Sprintf("删除失败:%s", err.Error()))
		o.Response(Failure, fmt.Sprintf("数据删除失败:%s", err.Error()), nil)
		return
	}
	o.Response(Success, "成功", c.ID)
}

// ImportCamera @Title 导入摄像头或一键告警设备
// @Description 导入摄像头或一键告警设备
// @Param   body  controllers.ReqImportCamera true "数据"
// @Success 0 {int} BaseResponse.Code "成功"
// @Failure 1 {int} BaseResponse.Code "失败"
// @router /v1/import [post]
func (o *CameraController) ImportCamera() {
	var obj ReqImportCamera
	if err := json.Unmarshal(o.Ctx.Input.RequestBody, &obj); err != nil {
		beego.Debug(string(o.Ctx.Input.RequestBody))
		o.Response(Failure, fmt.Sprintf("数据包解析错误:%s", err.Error()), nil)
		return
	}
	if err := obj.Validate(); err != nil {
		beego.Error(fmt.Sprintf("请求参数验证失败:%s", err.Error()))
		o.Response(Failure, fmt.Sprintf("请求参数验证失败:%s", err.Error()), nil)
		return
	}

	var resp []RespImport
	for _, v := range obj.List {
		var aresp RespImport
		aresp.Code = v.Code
		oo := models.CameraDevice{
			ID:      v.Code, //设备ID，DID
			Name:    v.Name, //集控器名称
			GID:     v.GID,  //网关ID
			DevType: v.DevType,
			Tenant:  obj.Tenant,
			State:   v.State, //1启用，0禁用
		}
		err := oo.SaveFromWeb()
		if err != nil {
			aresp.Error = err.Error()
			beego.Error(fmt.Sprintf("数据导入失败,code=%s,失败原因:%s", v.Code, err.Error()))
		}
		resp = append(resp, aresp)
	}
	o.Response(Success, "成功", resp)
}

// UpdateState @Title 更新摄像机或一键告警设备启用禁用状态
// @Description 更新摄像机或一键告警设备启用禁用状态
// @Param    body controllers.ProgramsState true "数据"
// @Success 0 {int} BaseResponse.Code "成功"
// @Failure 1 {int} BaseResponse.Code "失败"
// @router /v1/update/state [POST]
func (o *CameraController) UpdateState() {
	code := o.GetString("code")
	state, err := o.GetUint8("state", 0)
	if code == "" || state > 1 || err != nil {
		beego.Error("参数错误")
		o.Response(Failure, "参数错误", nil)
		return
	}
	err = models.CameraDeviceUpdateState(code, int(state))
	if err != nil {
		beego.Error("CameraController.UpdateState:更新设备状态错误:", err.Error())
		o.Response(Failure, "失败", err.Error())
		return
	}
	o.Response(Success, "成功", code)
}

// SelectPresets @Title 获取预置位信息
// @Description 获取预置位信息
// @Param   body controllers.ProgramsState true "数据"
// @Success 0 {int} BaseResponse.Code "成功"
// @Failure 1 {int} BaseResponse.Code "失败"
// @router /v1/select/presets [get]
func (o *CameraController) SelectPresets() {
	code := o.GetString("code")
	if code == "" {
		beego.Error("参数错误")
		o.Response(Failure, "参数错误", nil)
		return
	}
	arr, err := models.GetPresetsByID(code)
	if err != nil {
		beego.Error("CameraController.SelectPresets:获取预置位错误:", err.Error())
		o.Response(Failure, "失败", err.Error())
		return
	}
	presetdir := BaseUrl + string(os.PathSeparator) + "file" + string(os.PathSeparator) + "preset" + string(os.PathSeparator)
	var ret RespPresetInfo
	ret.Code = code
	for _, v := range arr {
		ret.Data = append(ret.Data, PresetInfo{Token: v.Token, Name: v.Name, File: presetdir + v.File})
	}
	o.Response(Success, "成功", ret)
}

// ControlPTZ @Title 球机PTZ控制
// @Description 球机PTZ控制
// @router /v1/control/ptz [get]
func (o *CameraController) ControlPTZ() {
	code := o.GetString("code")                //设备编号
	direction, err0 := o.GetUint8("direction") //方向,0~6
	speed, err1 := o.GetUint8("speed")         //速度
	if err0 != nil || err1 != nil || code == "" {
		beego.Error(fmt.Sprintf("请输入正确的参数"))
		o.Response(Failure, "参数错误", nil)
		return
	}

	if direction < 0 || direction > 6 || speed < 1 {
		beego.Error(fmt.Sprintf("请输入正确的参数"))
		o.Response(Failure, "参数错误", nil)
		return
	}

	ci, err := models.GetCameraInfoByID(code)
	if err != nil {
		beego.Error(fmt.Sprintf("找不到该摄像机的配置[ID=%s] err=%s", code, err))
		o.Response(Failure, fmt.Sprintf("找不到该摄像机的配置[ID=%s]", code), nil)
		return
	}
	var ptz protocol.Pack_PTZMoveInfo
	if str, err := ptz.EnCode(code, ci.GID, "", GetNextUint64(), direction, speed); err == nil {
		topic := GetTopic(ci.Tenant, protocol.DT_IPC, code, protocol.TP_ONVIF_PTZ)
		err := GetMqttHandler().PublishString(topic, str, mqtt.AtMostOnce)
		if err != nil {
			beego.Error(fmt.Sprintf("发布PTZ主题[%s]失败:%s", topic, err.Error()))
			o.Response(Failure, fmt.Sprintf("发布PTZ主题[%s]失败:%s", topic, err.Error()), nil)
			return
		} else {
			beego.Info(fmt.Sprintf("发布PTZ主题[%s]成功，内容:%s", topic, str))
		}
	}
	o.Response(Success, "成功", nil)
}

// ControlPTZHome @Title 球机HOME设置和跳转
// @Description 球机HOME设置和跳转
// @router /v1/control/ptzhome [get]
func (o *CameraController) ControlPTZHome() {
	code := o.GetString("code")      //设备编号
	flag, err0 := o.GetUint8("flag") //0跳转，1设置
	if err0 != nil || code == "" {
		beego.Error(fmt.Sprintf("请输入正确的参数"))
		o.Response(Failure, "参数错误", nil)
		return
	}
	if flag == 1 {
		flag = 4
	} else {
		flag = 5
	}
	ci, err := models.GetCameraInfoByID(code)
	if err != nil {
		beego.Error(fmt.Sprintf("找不到该摄像机的配置[ID=%s]", code))
		o.Response(Failure, fmt.Sprintf("找不到该摄像机的配置[ID=%s]", code), nil)
		return
	}
	var ptzc protocol.Pack_PTZCommonInfo
	if str, err := ptzc.EnCode(code, ci.GID, "", "1", "home", GetNextUint64(), int(flag)); err == nil {
		topic := GetTopic(ci.Tenant, protocol.DT_IPC, code, protocol.TP_ONVIF_PTZ_COMM)
		err := GetMqttHandler().PublishString(topic, str, mqtt.AtMostOnce)
		if err != nil {
			beego.Error(fmt.Sprintf("发布PTZ主题[%s]失败:%s", topic, err.Error()))
			o.Response(Failure, fmt.Sprintf("发布PTZ主题[%s]失败:%s", topic, err.Error()), nil)
			return
		}
	}
	o.Response(Success, "成功", nil)
}

//case 25: //设置home
//var ptzc protocol.Pack_PTZCommonInfo
//if str, err := ptzc.EnCode(SXTDID, GID, "", "10", "旁边的小路口", GetNextUint64(), 4); err == nil {
//mc.Publish(GetTopic(SXTDID, protocol.DT_IPC, protocol.TP_ONVIF_PTZ_COMM), 0x00, false, str).Wait()
//}
//case 26: //跳转home
//var ptzc protocol.Pack_PTZCommonInfo
//if str, err := ptzc.EnCode(SXTDID, GID, "", "10", "旁边的小路口", GetNextUint64(), 5); err == nil {
//mc.Publish(GetTopic(SXTDID, protocol.DT_IPC, protocol.TP_ONVIF_PTZ_COMM), 0x00, false, str).Wait()
//}

// ControlSnapshot @Title 抓图命令
// @Description 抓图命令
// @router /v1/control/snapshot [get]
func (o *CameraController) ControlSnapshot() {
	code := o.GetString("code") //设备编号
	if code == "" {
		beego.Error(fmt.Sprintf("请输入正确的参数"))
		o.Response(Failure, "参数错误", nil)
		return
	}
	ci, err := models.GetCameraInfoByID(code)
	if err != nil {
		beego.Error(fmt.Sprintf("找不到该摄像机的配置[ID=%s]", code))
		o.Response(Failure, fmt.Sprintf("找不到该摄像机的配置[ID=%s]", code), nil)
		return
	}
	var pc protocol.Pack_MediaCommonInfo
	if str, err := pc.EnCode(code, ci.GID, "", GetNextUint64(), 0 /*0:抓图*/); err == nil {
		topic := GetTopic(ci.Tenant, protocol.DT_IPC, code, protocol.TP_ONVIF_SNAPSHOT)
		err := GetMqttHandler().PublishString(topic, str, mqtt.AtMostOnce)
		if err != nil {
			beego.Error(fmt.Sprintf("发布PTZ主题[%s]失败:%s", topic, err.Error()))
			o.Response(Failure, fmt.Sprintf("发布PTZ主题[%s]失败:%s", topic, err.Error()), nil)
			return
		}
	}
	o.Response(Success, "成功", nil)
}

// Video @Title 视频推流、停止推流
// @Description 视频推流、停止推流
// @router /v1/control/video [get]
func (o *CameraController) Video() {
	code := o.GetString("code")     //设备编号
	flag, err := o.GetUint8("flag") //方向,0~6
	if err != nil || code == "" {
		beego.Error(fmt.Sprintf("请输入正确的参数"))
		o.Response(Failure, "参数错误", nil)
		return
	}
	if flag != 1 && flag != 2 {
		beego.Error(fmt.Sprintf("请输入正确的参数"))
		o.Response(Failure, "参数错误", nil)
		return
	}

	ci, err := models.GetCameraInfoByID(code)
	if err != nil {
		beego.Error(fmt.Sprintf("找不到该摄像机的配置[ID=%s]", code))
		o.Response(Failure, fmt.Sprintf("找不到该摄像机的配置[ID=%s]", code), nil)
		return
	}

	//开启GB28181协议的设备，视频流直接走GB28181服务，不需要边缘端用ffmpeg拉流、推流
	if ci.Gb28181 {
		beego.Debug(fmt.Sprintf("该摄象机配置了GB28181，不需要ffmpeg推流[设备ID=%s]", code))
		o.Response(Success, "成功", nil)
		return
	}
	var pc protocol.Pack_MediaCommonInfo
	if str, err := pc.EnCode(code, ci.GID, "", GetNextUint64(), int(flag)); err == nil {
		topic := GetTopic(ci.Tenant, protocol.DT_IPC, code, protocol.TP_ONVIF_VIDEO)
		err := GetMqttHandler().PublishString(topic, str, mqtt.AtMostOnce)
		if err != nil {
			beego.Error(fmt.Sprintf("发布PTZ主题[%s]失败:%s", topic, err.Error()))
			o.Response(Failure, fmt.Sprintf("发布PTZ主题[%s]失败:%s", topic, err.Error()), nil)
			return
		}
	}
	o.Response(Success, "成功", nil)
}

// Snapshot @Title 抓图上报
// @Description 摄像头抓图上报
// @router /v1/snapshot [post]
func (o *CameraController) Snapshot() {
	f, fh, err := o.Ctx.Request.FormFile("file")
	if err != nil {
		return
	}
	defer f.Close()
	f.Seek(0, 0)
	content, err := ioutil.ReadAll(f)
	if err != nil {
		return
	}

	strDate := util.MlNow().Format("2006-01-02")
	filepath := "file" + string(os.PathSeparator) + "snapshot" + string(os.PathSeparator)
	strList := strings.Split(fh.Filename, "_")
	if len(strList) >= 2 {
		filepath = filepath + strList[0] + string(os.PathSeparator)
		strList2 := strings.Split(strList[1], ".")
		if len(strList2) > 0 {
			i64, err := strconv.ParseInt(strList2[0], 10, 64)
			if err == nil {
				strDate = time.Unix(i64, 0).Format("2006-01-02")
			}
		}
	}
	filepath = filepath + strDate + string(os.PathSeparator)
	os.MkdirAll(filepath, os.ModePerm)
	filepath = filepath + fh.Filename
	err = ioutil.WriteFile(filepath, content, os.ModePerm)
	if err != nil {
		beego.Error("Snapshot存储抓图发生错误:", err)
	}
}

// PresetPicture @Title 预置位图片上传
// @Description 预置位图片上传
// @router /v1/preset/picture [post]
func (o *CameraController) PresetPicture() {
	f, fh, err := o.Ctx.Request.FormFile("file")
	if err != nil {
		return
	}
	defer f.Close()
	f.Seek(0, 0)
	content, err := ioutil.ReadAll(f)
	if err != nil {
		return
	}
	filepath := "file" + string(os.PathSeparator) + "preset" + string(os.PathSeparator)
	os.MkdirAll(filepath, os.ModePerm)
	filepath = filepath + fh.Filename
	err = ioutil.WriteFile(filepath, content, os.ModePerm)
	if err != nil {
		beego.Error("Snapshot存储抓图发生错误:", err)
	}
}

// FLV @Title 上传MP4
// @Description 上传MP4
// @router /v1/flv [post]
func (o *CameraController) FLV() {
	f, fh, err := o.Ctx.Request.FormFile("file")
	if err != nil {
		return
	}
	defer f.Close()
	f.Seek(0, 0)
	content, err := ioutil.ReadAll(f)
	if err != nil {
		return
	}

	strDate := util.MlNow().Format("2006-01-02")
	filepath := "file" + string(os.PathSeparator) + "flv" + string(os.PathSeparator)
	strList := strings.Split(fh.Filename, "_")
	if len(strList) >= 2 {
		filepath = filepath + strList[0] + string(os.PathSeparator)
		strList2 := strings.Split(strList[1], ".")
		if len(strList2) > 0 {
			i64, err := strconv.ParseInt(strList2[0], 10, 64)
			if err == nil {
				strDate = time.Unix(i64, 0).Format("2006-01-02")
			}
		}
	}
	filepath = filepath + strDate + string(os.PathSeparator)
	os.MkdirAll(filepath, os.ModePerm)
	filepath = filepath + fh.Filename
	err = ioutil.WriteFile(filepath, content, os.ModePerm)
	if err != nil {
		beego.Error("MP4存储发生错误:", err)
	}
}

// GetSnapshot @Title 获取抓取的图片
// @Description 获取抓取的图片
// @router /v1/select/snapshot [get]
func (o *CameraController) GetSnapshot() {
	code := o.GetString("code") //设备编号
	date := o.GetString("date") //
	if code == "" {
		beego.Error(fmt.Sprintf("请输入正确的参数"))
		o.Response(Failure, "参数错误", nil)
		return
	}
	if date == "" {
		date = util.MlNow().Format("2006-01-02")
	}
	//dir := "file/snapshot/" + util.MlNow().Format("2006-01-02") + "/"
	filepath := "file" + string(os.PathSeparator) + "snapshot" + string(os.PathSeparator) +
		code + string(os.PathSeparator) + date + string(os.PathSeparator)
	fis, err := ioutil.ReadDir(filepath)
	if err != nil {
		beego.Error(fmt.Sprintf("未找到该路径[%s]，请先抓图", err.Error()))
		o.Response(Failure, "未找到该路径，请先抓图", nil)
		return
	}
	var obj RespSnapshotUrl
	obj.Code = code
	obj.Date = date
	for _, v := range fis {
		if strings.HasSuffix(v.Name(), ".jpg") { //只返回jpg图片
			jpgUrl := BaseUrl + "/file/snapshot/" + code + "/" + date + "/" + v.Name()
			obj.URLs = append(obj.URLs, jpgUrl)
		}
	}
	o.Response(Success, "成功", obj)
}

// GetFLV @Title 获取录制的视频
// @Description 获取录制的视频
// @router /v1/select/flv [get]
func (o *CameraController) GetFLV() {
	code := o.GetString("code") //设备编号
	date := o.GetString("date") //
	if code == "" {
		beego.Error(fmt.Sprintf("请输入正确的参数"))
		o.Response(Failure, "参数错误", nil)
		return
	}
	if date == "" {
		date = util.MlNow().Format("2006-01-02")
	}
	//dir := "file/snapshot/" + util.MlNow().Format("2006-01-02") + "/"
	filepath := "file" + string(os.PathSeparator) + "flv" + string(os.PathSeparator) +
		code + string(os.PathSeparator) + date + string(os.PathSeparator)
	fis, err := ioutil.ReadDir(filepath)
	if err != nil {
		beego.Error(fmt.Sprintf("未找到该路径[%s]，请先录制mp4", err.Error()))
		o.Response(Failure, "未找到该路径，请先录制mp4", nil)
		return
	}
	var obj RespSnapshotUrl
	obj.Code = code
	obj.Date = date
	for _, v := range fis {
		if strings.HasSuffix(v.Name(), ".mp4") { //只返回jpg图片
			jpgUrl := BaseUrl + "/file/mp4/" + code + "/" + date + "/" + v.Name()
			obj.URLs = append(obj.URLs, jpgUrl)
		}
	}
	o.Response(Success, "成功", obj)
}
