//升级模块
// 秒寻科技
// zt 2024-11-11

package upgrade

import (
	"bytes"
	"encoding/binary"
	"ipsomc/module/ps/psdl"
	"ipsomc/module/ps/psmodel"
	"ipsomc/module/ps/pspublic"
	"ipsomc/module/uploadfile/uploadfilemodel"
	"ipsomc/public"
)

var (
	Gupgrade_stManageParam OAM_UPGRADE_T //升级管理模块
	Gupgrade_abyFileData   []byte        //升级文件
)

// 升级
type Upgrade struct {
	psDlApi psdl.PsDl
}

// 升级管理结构
type OAM_UPGRADE_T struct {
	UpgradeStatus uint8  `json:"upgrade_status"` //1：升级中，2：升级成功，3：升级失败，4：超时
	FileSize      uint32 `json:"file_size"`      //升级文件大小
	TotalTimes    uint32 `json:"total_times"`    //发送总次数
	SendingTimes  uint32 `json:"sending_times"`  //当前发送次数
	RemainderLen  uint32 `json:"remainder_len"`  //最后一次数据长度
}

// 远程升级，接收文件
func (obj *Upgrade) UpgradeReceiveFile(dataModel *uploadfilemodel.UploadFileParam, dataList []byte, dwLen uint32) error {
	fileSize := dwLen //文件长度

	//4字节对齐
	remainderLen := fileSize % 4 //余数
	if remainderLen > 0 {
		fileSize = fileSize + 4 - remainderLen
	}
	Gupgrade_stManageParam.FileSize = fileSize //文件字节数

	//保存文件数据
	Gupgrade_abyFileData = make([]byte, fileSize)
	copy(Gupgrade_abyFileData, dataList)

	//组织升级管理结构
	totalTimes := fileSize / psmodel.PS_MCP_B_DATA_LEN
	remainderLen = fileSize % psmodel.PS_MCP_B_DATA_LEN
	if remainderLen > 0 {
		Gupgrade_stManageParam.TotalTimes = totalTimes + 1
		Gupgrade_stManageParam.RemainderLen = remainderLen
	} else {
		Gupgrade_stManageParam.TotalTimes = totalTimes
		Gupgrade_stManageParam.RemainderLen = psmodel.PS_MCP_B_DATA_LEN
	}

	//切换工作模式
	if err := obj.UpgradeSwitchSoftMode(dataModel, 1); err != nil {
		return err
	}

	return nil
}

// 组织设置LTV数据结构
func (obj *Upgrade) UpgradeOrganizeLtv(wLen uint16, wCmd uint16, value []byte, dataList *bytes.Buffer) {
	dataLen := wLen - 4
	tmpSlice := make([]byte, dataLen) //定义该命令所需的数据切片
	copy(tmpSlice, value)

	binary.Write(dataList, binary.LittleEndian, wLen)     //长度
	binary.Write(dataList, binary.LittleEndian, wCmd)     //命令
	binary.Write(dataList, binary.LittleEndian, tmpSlice) //数据
}

// 切换升级模式
// dataModel:升级文件参数
// upgradeStatus:升级状态
func (obj *Upgrade) UpgradeSwitchSoftMode(dataModel *uploadfilemodel.UploadFileParam, upgradeStatus uint8) error {
	var dataList bytes.Buffer
	tmpBytes := make([]byte, 1)

	//组织参数
	tmpBytes = append(tmpBytes, upgradeStatus)
	obj.UpgradeOrganizeLtv(5, uint16(pspublic.OAM_UPGRADE_SWITCH_SOFT_WORK_MODE), tmpBytes, &dataList) //切换软件模式

	//发送数据给基站
	if err := obj.psDlApi.PsDlSendOamDataToBts(int(dataModel.ProjectID), dataModel.BtsID, psmodel.MCP_B, psmodel.MCP_B_CONTROL, dataList.Bytes()); err != nil {
		obj.UpgradeDeinit()
		return err
	}

	Gupgrade_stManageParam.UpgradeStatus = 1 //升级中
	Gupgrade_stManageParam.SendingTimes = 0  //发送次数复位

	return nil
}

// 通知升级文件大小
// ProjectID:项目编号
// BtsID:基站编号
func (obj *Upgrade) UpgradeNoticeFileSize(ProjectID int, BtsID int) error {
	var dataList bytes.Buffer
	tmpBytes := make([]byte, 4)

	////组织参数
	binary.LittleEndian.PutUint32(tmpBytes, Gupgrade_stManageParam.FileSize)
	obj.UpgradeOrganizeLtv(8, uint16(pspublic.OAM_UPGRADE_NOTICE_FILE_SIZE_), tmpBytes, &dataList)

	//发送数据给基站
	if err := obj.psDlApi.PsDlSendOamDataToBts(ProjectID, BtsID, psmodel.MCP_B, psmodel.MCP_B_CONTROL, dataList.Bytes()); err != nil {
		obj.UpgradeDeinit()
		return err
	}

	return nil
}

// 传输升级文件
// ProjectID:项目编号
// BtsID:基站编号
func (obj *Upgrade) UpgradeTransferFile(ProjectID int, BtsID int) error {
	var payloadLen uint32 = psmodel.PS_MCP_B_DATA_LEN //载荷长度
	var dwStartIndex uint32 = 0
	var dwStopIndex uint32 = 0

	var dataList bytes.Buffer
	var dwLen uint32 = 0
	var stUpgradeStatus public.PublicUpgradeStatus //升级状态管理结构

	if Gupgrade_stManageParam.SendingTimes < Gupgrade_stManageParam.TotalTimes {
		if Gupgrade_stManageParam.SendingTimes == (Gupgrade_stManageParam.TotalTimes - 1) {
			payloadLen = Gupgrade_stManageParam.RemainderLen
		}
		payload := make([]byte, payloadLen) //载荷数据空间

		//组织数据
		dwStartIndex = Gupgrade_stManageParam.SendingTimes * psmodel.PS_MCP_B_DATA_LEN
		dwStopIndex = dwStartIndex + payloadLen
		copy(payload, Gupgrade_abyFileData[dwStartIndex:dwStopIndex])
		Gupgrade_stManageParam.SendingTimes++

		//组织ltv数据
		dwLen = 8 + payloadLen
		binary.Write(&dataList, binary.LittleEndian, uint16(dwLen))                                  //长度
		binary.Write(&dataList, binary.LittleEndian, uint16(pspublic.OAM_UPGRADE_SEND_UPGRADE_FILE)) //命令
		binary.Write(&dataList, binary.LittleEndian, Gupgrade_stManageParam.SendingTimes)            //帧号
		binary.Write(&dataList, binary.LittleEndian, payload)                                        //数据

		//发送数据给基站
		if err := obj.psDlApi.PsDlSendOamDataToBts(ProjectID, BtsID, psmodel.MCP_B, psmodel.MCP_B_FILE_TRANSFER, dataList.Bytes()); err != nil {
			obj.UpgradeDeinit()      //反向初始化
			stUpgradeStatus.Flag = 1 //失败
			stUpgradeStatus.Percent = 0
			public.Gpub_chanWebSocketMsg <- stUpgradeStatus //通过通道发送数据到http模块
			return err
		}

		stUpgradeStatus.Flag = 2 //成功
		stUpgradeStatus.Percent = uint8((Gupgrade_stManageParam.SendingTimes * 100) / Gupgrade_stManageParam.TotalTimes)
		public.Gpub_chanWebSocketMsg <- stUpgradeStatus //通过通道发送数据到http模块
	} else {
		//通知升级结束
		if err := obj.UpgradeNoticeOver(ProjectID, BtsID); err != nil {
			obj.UpgradeDeinit() //反向初始化

			stUpgradeStatus.Flag = 1 //失败
			stUpgradeStatus.Percent = 0
			public.Gpub_chanWebSocketMsg <- stUpgradeStatus //通过通道发送数据到http模块
			return err
		}

		obj.UpgradeDeinit() //反向初始化
	}
	return nil
}

// 通知升级结束
// ProjectID:项目编号
// BtsID:基站编号
func (obj *Upgrade) UpgradeNoticeOver(ProjectID int, BtsID int) error {
	var dataList bytes.Buffer
	tmpBytes := make([]byte, 4)

	////组织参数
	binary.LittleEndian.PutUint32(tmpBytes, Gupgrade_stManageParam.FileSize)
	obj.UpgradeOrganizeLtv(8, uint16(pspublic.OAM_UPGRADE_NOTICE_UPGRADE_OVER), tmpBytes, &dataList)

	//发送数据给基站
	if err := obj.psDlApi.PsDlSendOamDataToBts(ProjectID, BtsID, psmodel.MCP_B, psmodel.MCP_B_CONTROL, dataList.Bytes()); err != nil {
		obj.UpgradeDeinit() //反向初始化
		return err
	}

	obj.UpgradeDeinit() //反向初始化

	return nil
}

// 升级成功
func (obj *Upgrade) UpgradeSuccess() {
	obj.UpgradeDeinit() //反向初始化
}

// 升级模块反向初始化
func (obj *Upgrade) UpgradeDeinit() {
	Gupgrade_stManageParam = OAM_UPGRADE_T{}
}
