package logic

//处理websocket云渲染任务数据
import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"fusenapi/constants"
	"fusenapi/model/gmodel"
	"fusenapi/service/repositories"
	"fusenapi/utils/curl"
	"fusenapi/utils/hash"
	"fusenapi/utils/websocket_data"
	"github.com/zeromicro/go-zero/core/logx"
	"gorm.io/gorm"
	"strconv"
	"strings"
	"time"
)

var (
	//每个websocket渲染任务缓冲队列长度默认值
	renderChanLen = 500
	//每个websocket渲染并发数
	renderChanConcurrency = 100
)

// 渲染处理器
type renderProcessor struct {
}

// 云渲染属性
type extendRenderProperty struct {
	renderChan          chan websocket_data.RenderImageReqMsg //渲染消息入口的缓冲队列
	renderCtx           context.Context                       //渲染控制上下文(用于切换模板标签/颜色/logo取消之前发送的不相同的任务)
	renderCtxCancelFunc context.CancelFunc                    //渲染控制上下文取消方法
	selectColorIndex    int                                   //选择的颜色索引(用于标记连接当前连接选择的颜色)
	templateTag         string                                //模板标签 (用于标记连接当前连接选择的模板标签)
	Logo                string                                //logo地址 (用于标记连接当前连接选择的logo)
}

// 处理分发到这里的数据
func (r *renderProcessor) allocationMessage(w *wsConnectItem, data []byte) {
	//logx.Info("收到渲染任务消息")
	var renderImageData websocket_data.RenderImageReqMsg
	if err := json.Unmarshal(data, &renderImageData); err != nil {
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "数据格式错误", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
		logx.Error("invalid format of websocket render image message", err)
		return
	}
	//颜色/模板标签/logo变更
	ifCancelOldCtx := false
	if renderImageData.RenderData.TemplateTag != w.extendRenderProperty.templateTag {
		ifCancelOldCtx = true
	}
	if renderImageData.RenderData.TemplateTagColor.SelectedColorIndex != w.extendRenderProperty.selectColorIndex {
		ifCancelOldCtx = true
	}
	if renderImageData.RenderData.Logo != w.extendRenderProperty.Logo {
		ifCancelOldCtx = true
	}
	if ifCancelOldCtx {
		//赋值新的
		w.extendRenderProperty.templateTag = renderImageData.RenderData.TemplateTag
		w.extendRenderProperty.selectColorIndex = renderImageData.RenderData.TemplateTagColor.SelectedColorIndex
		w.extendRenderProperty.Logo = renderImageData.RenderData.Logo
		//让之前的失效
		w.extendRenderProperty.renderCtxCancelFunc()
		//重新赋值
		w.extendRenderProperty.renderCtx, w.extendRenderProperty.renderCtxCancelFunc = context.WithCancel(w.logic.ctx)
	}
	select {
	case <-w.closeChan: //已经关闭
		return
	case w.extendRenderProperty.renderChan <- renderImageData: //发入到缓冲队列
		return
	case <-time.After(time.Millisecond * 50): //超过50毫秒丢弃
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "渲染队列溢出,请稍后再发", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
		return
	}
}

// 消费渲染缓冲队列数据
func (w *wsConnectItem) consumeRenderImageData() {
	defer func() {
		if err := recover(); err != nil {
			logx.Error("func consumeRenderImageData panic:", err)
		}
	}()
	//限制并发
	limitChan := make(chan struct{}, renderChanConcurrency)
	defer close(limitChan)
	for {
		select {
		case <-w.closeChan: //已关闭
			return
		case data := <-w.extendRenderProperty.renderChan: //消费数据
			//标签不一样
			if data.RenderData.TemplateTag != w.extendRenderProperty.templateTag {
				//logx.Info("标签不一致,丢弃消息")
				continue
			}
			//颜色不一致
			if data.RenderData.TemplateTagColor.SelectedColorIndex != w.extendRenderProperty.selectColorIndex {
				//logx.Info("颜色不一致,丢弃消息")
				continue
			}
			//logo不一样
			if data.RenderData.Logo != w.extendRenderProperty.Logo {
				continue
			}
			limitChan <- struct{}{}
			go func(d websocket_data.RenderImageReqMsg) {
				defer func() {
					if err := recover(); err != nil {
						logx.Error("func renderImage main panic:", err)
					}
				}()
				//临时chan用select io多路复用去判断携程退出
				tmpChan := make(chan struct{}, 1)
				defer close(tmpChan)
				defer func() {
					<-limitChan
				}()
				go func() {
					defer func() {
						if err := recover(); err != nil {
							logx.Error("func renderImage panic:", err)
						}
					}()
					select {
					case <-w.extendRenderProperty.renderCtx.Done():
						panic("=========检测到模板标签/颜色变化,渲染取消旧的任务=======")
					case <-tmpChan:
						return
					}
				}()
				w.renderImage(d)
			}(data)
		}
	}
}

// 执行渲染任务
func (w *wsConnectItem) renderImage(renderImageData websocket_data.RenderImageReqMsg) {
	/*	if renderImageData.RenderData.Logo == "" {
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "请传入logo", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
		return
	}*/
	if !strings.Contains(renderImageData.RenderData.Logo, "storage.fusenpack.com") {
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "非法的logo", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
		return
	}
	//没传分辨率
	if renderImageData.RenderData.Resolution == "" {
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "请传入合图分辨率", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
		return
	}
	//分辨率校验
	resolution, err := strconv.Atoi(renderImageData.RenderData.Resolution)
	if err != nil {
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "请传入正确的合图分辨率格式", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
		return
	}
	if resolution < 512 || resolution > 2048 {
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "请传入正确的合图分辨率范围值(512~2048)", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
		return
	}
	lenColor := len(renderImageData.RenderData.TemplateTagColor.Colors)
	if lenColor == 0 {
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "请传入模板标签选择的颜色", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
		return
	}
	if renderImageData.RenderData.TemplateTagColor.SelectedColorIndex >= lenColor || renderImageData.RenderData.TemplateTagColor.SelectedColorIndex < 0 {
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "选择的模板标签颜色索引越界", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
		return
	}
	//获取产品信息(部分字段)
	productInfo, err := w.logic.svcCtx.AllModels.FsProduct.FindOne(w.logic.ctx, renderImageData.RenderData.ProductId, "id,is_customization")
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "该产品不存在", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
			return
		}
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "获取产品失败", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
		logx.Error(err)
		return
	}
	//不可定制
	if *productInfo.IsCustomization == 0 {
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "该产品不可定制", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
		return
	}
	//用户id赋值
	renderImageData.RenderData.UserId = w.userId
	renderImageData.RenderData.GuestId = w.guestId
	//获取信息
	productSize, productTemplate, model3dInfo, err := w.getProductRelateionInfo(&renderImageData)
	if err != nil {
		logx.Error(err)
		return
	}
	//获取渲染设置信息
	element, err := w.logic.svcCtx.AllModels.FsProductTemplateElement.FindOneByModelId(w.logic.ctx, *productTemplate.ElementModelId)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "云渲染设置不存在", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3dInfo.Id, productSize.Id, *productTemplate.ElementModelId)
			logx.Error("element info is not found,element_model_id = ", *productTemplate.ElementModelId)
			return
		}
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "获取云渲染设置失败", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3dInfo.Id, productSize.Id, *productTemplate.ElementModelId)
		logx.Error("failed to get element ,", err)
		return
	}

	//获取刀版图
	combineReq := repositories.LogoCombineReq{
		UserId:                   renderImageData.RenderData.UserId,
		GuestId:                  renderImageData.RenderData.GuestId,
		ProductTemplateV2Info:    productTemplate,
		ProductTemplateTagGroups: renderImageData.RenderData.TemplateTagGroups,
		TemplateTag:              renderImageData.RenderData.TemplateTag,
		Website:                  renderImageData.RenderData.Website,
		Slogan:                   renderImageData.RenderData.Slogan,
		Address:                  renderImageData.RenderData.Address,
		Phone:                    renderImageData.RenderData.Phone,
		Qrcode:                   renderImageData.RenderData.Qrcode,
		LogoUrl:                  renderImageData.RenderData.Logo,
		TemplateTagColor: repositories.TemplateTagColor{
			Color: renderImageData.RenderData.TemplateTagColor.Colors,
			Index: renderImageData.RenderData.TemplateTagColor.SelectedColorIndex,
		},
		Resolution: renderImageData.RenderData.Resolution,
	}
	res, err := w.logic.svcCtx.Repositories.ImageHandle.LogoCombine(w.logic.ctx, &combineReq)
	if err != nil {
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "合成刀版图失败:"+err.Error(), renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3dInfo.Id, productSize.Id, *productTemplate.ElementModelId)
		logx.Error("合成刀版图失败,合成请求数据:", combineReq, "错误信息:", err)
		return
	}
	combineImage := "" //刀版图
	if res != nil && res.ResourceUrl != nil {
		combineImage = *res.ResourceUrl
	} else {
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "合成的刀版图是空的地址", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3dInfo.Id, productSize.Id, *productTemplate.ElementModelId)
		logx.Error("合成刀版图失败,合成的刀版图是空指针:", err)
		return
	}
	//发送合图完毕阶段消息
	w.sendCombineImageStepResponseMessage(renderImageData.RenderId, renderImageData.RequestId, combineImage, productSize.Id, model3dInfo.Id, productTemplate.Id, res.DiffTimeLogoCombine, res.DiffTimeUploadFile)
	//获取唯一id
	taskId := w.genRenderTaskId(combineImage, renderImageData, model3dInfo, productTemplate, element)
	//查询有没有缓存的资源,有就返回
	resource, err := w.logic.svcCtx.AllModels.FsResource.FindOneById(w.logic.ctx, taskId)
	if err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, taskId, "获取unity云渲染缓存失败", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3dInfo.Id, productSize.Id, *productTemplate.ElementModelId)
			logx.Error("failed to find render resource:", err)
			return
		}
		logx.Info("无缓存的渲染图,需要unity")
	} else {
		//返回给客户端
		w.sendRenderResultData(websocket_data.RenderImageRspMsg{
			RenderId:  renderImageData.RenderId,
			RequestId: renderImageData.RequestId,
			Image:     *resource.ResourceUrl,
			RenderProcessTime: websocket_data.RenderProcessTime{
				UnityRenderTakesTime:            "cache",
				UploadUnityRenderImageTakesTime: "cache",
			},
		})
		return
	}
	//组装数据
	if err = w.assembleRenderDataToUnity(taskId, combineImage, renderImageData, productTemplate, model3dInfo, element, productSize); err != nil {
		logx.Error("组装数据失败:", err)
		return
	}
}

// 获取模板相关信息
func (w *wsConnectItem) getProductRelateionInfo(renderImageData *websocket_data.RenderImageReqMsg) (productSize *gmodel.FsProductSize, productTemplate *gmodel.FsProductTemplateV2, model3d *gmodel.FsProductModel3d, err error) {
	//获取模板
	productTemplate, err = w.logic.svcCtx.AllModels.FsProductTemplateV2.FindOneCloudRenderByProductIdTemplateTag(w.logic.ctx, renderImageData.RenderData.ProductId, renderImageData.RenderData.TemplateTag, "sort ASC")
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "找不到对应开启云渲染模板", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
			return nil, nil, nil, errors.New("找不到对应开启云渲染模板")
		}
		logx.Error(err)
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "获取对应开启云渲染模板失败", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
		return nil, nil, nil, errors.New("获取对应开启云渲染模板失败")
	}
	if productTemplate.TemplateInfo == nil || *productTemplate.TemplateInfo == "" {
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "模板设计信息是空的", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, 0, 0, 0)
		return nil, nil, nil, errors.New("模板设计信息是空的")
	}
	//根据模板找到模型
	model3d, err = w.logic.svcCtx.AllModels.FsProductModel3d.FindOne(w.logic.ctx, *productTemplate.ModelId)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "找不到对应模型", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, 0, 0, 0)
			return nil, nil, nil, errors.New("找不到对应模型")
		}
		logx.Error(err)
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "获取对应模型失败", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, 0, 0, 0)
		return nil, nil, nil, errors.New("获取对应模型失败")
	}
	if model3d.ModelInfo == nil || *model3d.ModelInfo == "" {
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "模型设计信息是空的", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3d.Id, 0, 0)
		return nil, nil, nil, errors.New("模型设计信息是空的")
	}
	//根据模型id获取尺寸信息
	productSize, err = w.logic.svcCtx.AllModels.FsProductSize.FindOne(w.logic.ctx, *model3d.SizeId)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "找不到对应尺寸", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3d.Id, 0, 0)
			return nil, nil, nil, errors.New("找不到对应尺寸")
		}
		logx.Error(err)
		w.renderErrResponse(renderImageData.RenderId, renderImageData.RequestId, renderImageData.RenderData.TemplateTag, "", "获取对应尺寸失败", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3d.Id, 0, 0)
		return nil, nil, nil, errors.New("获取对应尺寸失败")
	}
	return
}

// 组装数据发送给unity
func (w *wsConnectItem) assembleRenderDataToUnity(taskId string, combineImage string, info websocket_data.RenderImageReqMsg, productTemplate *gmodel.FsProductTemplateV2, model3dInfo *gmodel.FsProductModel3d, element *gmodel.FsProductTemplateElement, productSize *gmodel.FsProductSize) (err error) {
	//组装数据
	refletion := -1
	if element.Refletion != nil && *element.Refletion != "" {
		refletion, err = strconv.Atoi(*element.Refletion)
		if err != nil {
			logx.Error("err refletion")
			w.renderErrResponse(info.RenderId, info.RequestId, info.RenderData.TemplateTag, taskId, "解析云渲染设置,把Refletion字段值从字符串转数字失败", info.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3dInfo.Id, productSize.Id, *productTemplate.ElementModelId)
			return err
		}
	}
	//组装data数据
	var mode map[string]interface{}
	if element.Mode != nil && *element.Mode != "" {
		if err = json.Unmarshal([]byte(*element.Mode), &mode); err != nil {
			logx.Error("faile to parse element mode json:", err)
			w.renderErrResponse(info.RenderId, info.RequestId, info.RenderData.TemplateTag, taskId, "解析云渲染设置字段 mode 为map对象失败", info.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3dInfo.Id, productSize.Id, *productTemplate.ElementModelId)
			return err
		}
	}
	tempData := make([]map[string]interface{}, 0, 3)
	if element.Base != nil && *element.Base != "" {
		tempData = append(tempData, map[string]interface{}{
			"name":      "model",
			"data":      "0," + combineImage + "," + *element.Base + " " + fmt.Sprintf("%d", time.Now().UTC().UnixMilli()),
			"type":      "other",
			"layer":     "0",
			"is_update": 1,
			"mode":      mode["model"],
		})
	}
	if element.Shadow != nil && *element.Shadow != "" {
		tempData = append(tempData, map[string]interface{}{
			"name":      "shadow",
			"data":      *element.Shadow,
			"type":      "other",
			"layer":     "0",
			"is_update": 0,
			"mode":      mode["shadow"],
		})
	}
	if element.ModelP != nil && *element.ModelP != "" {
		tempData = append(tempData, map[string]interface{}{
			"name":      "model_P",
			"data":      "0," + *element.ModelP,
			"type":      "other",
			"layer":     "0",
			"is_update": 0,
			"mode":      mode["model_P"],
		})
	}
	result := []interface{}{
		map[string]interface{}{
			"light":     *element.Light,
			"refletion": refletion,
			"scale":     *element.Scale,
			"sku_id":    info.RenderData.ProductId,
			"tid":       *element.Title,
			"rotation":  *element.Rotation,
			"filePath":  "", //todo 文件路径,针对千人千面
			"data":      tempData,
		},
	}
	//发送运行阶段消息(组装数据)
	w.sendAssembleRenderDataStepResponseMessage(info.RenderId, info.RequestId)
	temId := websocket_data.ToUnityIdStruct{
		TaskId:          taskId,
		Wid:             w.uniqueId,
		RenderId:        info.RenderId,
		RequestId:       info.RequestId,
		RenderBeginTime: time.Now().UTC().UnixMilli(),
		TemplateTag:     info.RenderData.TemplateTag,
	}
	temIdBytes, _ := json.Marshal(temId)
	sendData := map[string]interface{}{
		"id":               string(temIdBytes),
		"order_id":         0,
		"user_id":          info.RenderData.UserId,
		"guest_id":         info.RenderData.GuestId,
		"sku_ids":          []int64{info.RenderData.ProductId},
		"tids":             []string{*element.Title},
		"data":             result,
		"is_thousand_face": 0,
		"folder":           "", //todo 千人千面需要使用
	}
	//请求unity接口
	url := w.logic.svcCtx.Config.Unity.Host + "/api/render/queue/push"
	header := make(map[string]string)
	header["content-type"] = "application/json"
	postData := map[string]interface{}{
		"group":       "unity3d",
		"source":      "home page",
		"priority":    1,
		"create_at":   time.Now().UTC(),
		"render_data": sendData,
	}
	postDataBytes, _ := json.Marshal(postData)
	_, err = curl.ApiCall(url, "POST", header, bytes.NewReader(postDataBytes), time.Second*10)
	if err != nil {
		w.renderErrResponse(info.RenderId, info.RequestId, info.RenderData.TemplateTag, taskId, "请求unity接口失败", info.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3dInfo.Id, productSize.Id, *productTemplate.ElementModelId)
		logx.Error("failed to send data to unity")
		return err
	}
	//发送运行阶段消息
	w.sendRenderDataToUnityStepResponseMessage(info.RenderId, info.RequestId)
	logx.Info("发送到unity成功,刀版图:", combineImage /*, "  请求unity的数据:", string(postDataBytes)*/)
	return nil
}

// 组装渲染任务id
func (w *wsConnectItem) genRenderTaskId(combineImage string, renderImageData websocket_data.RenderImageReqMsg, model3dInfo *gmodel.FsProductModel3d, productTemplate *gmodel.FsProductTemplateV2, element *gmodel.FsProductTemplateElement) string {
	//生成任务id(需要把user_id,guest_id设为0)
	incomeHashParam := renderImageData.RenderData
	incomeHashParam.UserId = 0  //设为0(渲染跟用户id无关)
	incomeHashParam.GuestId = 0 //设为0(渲染跟用户id无关)
	incomeHashBytes, _ := json.Marshal(incomeHashParam)
	modelHashStr := ""
	templateHashStr := ""
	if model3dInfo.ModelInfo != nil {
		modelHashStr = *model3dInfo.ModelInfo
	}
	if productTemplate.TemplateInfo != nil {
		templateHashStr = *productTemplate.TemplateInfo
	}
	elementHashBytes, _ := json.Marshal(element)
	hashMap := map[string]interface{}{
		"income_param":   incomeHashBytes,
		"model_info":     modelHashStr,
		"template_info":  templateHashStr,
		"material_image": *productTemplate.MaterialImg,
		"render_element": elementHashBytes,
		"combine_image":  combineImage,
	}
	return hash.JsonHashKey(hashMap)
}

// ****************************下面的发送消息的*********************************
// 发送合图完毕阶段通知消息
func (w *wsConnectItem) sendCombineImageStepResponseMessage(renderId, requestId, combineImage string, sizeId, modelId, templateId, combineTime, uploadTime int64) {
	if w.openDebug {
		combineTakesTime := "cache"
		uploadCombineImageTakesTime := "cache"
		if combineTime > 0 {
			combineTakesTime = fmt.Sprintf("%dms", combineTime)
		}
		if uploadTime > 0 {
			uploadCombineImageTakesTime = fmt.Sprintf("%dms", uploadTime)
		}
		w.sendToOutChan(w.respondDataFormat(constants.WEBSOCKET_COMBINE_IMAGE, websocket_data.CombineImageRspMsg{
			RenderId:     renderId,
			RequestId:    requestId,
			CombineImage: combineImage,
			SizeId:       sizeId,
			ModelId:      modelId,
			TemplateId:   templateId,
			CombineProcessTime: websocket_data.CombineProcessTime{
				CombineTakesTime:            combineTakesTime,
				UploadCombineImageTakesTime: uploadCombineImageTakesTime,
			},
		}))
	}
}

// 发送组装unity需要的数据完毕消息
func (w *wsConnectItem) sendAssembleRenderDataStepResponseMessage(renderId string, requestId string) {
	if w.openDebug {
		w.sendToOutChan(w.respondDataFormat(constants.WEBSOCKET_ASSEMBLE_RENDER_DATA, websocket_data.ToUnityRspMsg{RenderId: renderId, RequestId: requestId}))
	}
}

// 发送组装数据到unity完毕阶段通知消息
func (w *wsConnectItem) sendRenderDataToUnityStepResponseMessage(renderId string, requestId string) {
	if w.openDebug {
		w.sendToOutChan(w.respondDataFormat(constants.WEBSOCKET_SEND_DATA_TO_UNITY, websocket_data.AssembleRenderDataRspMsg{RenderId: renderId, RequestId: requestId}))
	}
}

// 发送渲染最终结果数据到前端
func (w *wsConnectItem) sendRenderResultData(data websocket_data.RenderImageRspMsg) {
	w.sendToOutChan(w.respondDataFormat(constants.WEBSOCKET_RENDER_IMAGE, data))
}