local GFunc = {}

local EMPTY_TABLE = {}
local TEMP_LIST = {}
local TEMP_MAP = {}
local HASH_SEED = 31
local UINT_RANGE = 4294967296
local byte = string.byte
local DESIGN_RESOLUTION_WIDTH = CS.BF.GameConst.DESIGN_RESOLUTION_WIDTH
local DESIGN_RESOLUTION_HEIGHT = CS.BF.GameConst.DESIGN_RESOLUTION_HEIGHT
local UnityTime = CS.UnityEngine.Time

--获取unicode 字符串长度 和utf-8长度
function GFunc.getTextLen(str)
	local byteLen = #str
	local strLen = 0
	local i = 1
	local curByte
	local byteCount = 1
	while i <= byteLen do
		curByte = byte(str, i)
		byteCount = 1
		if curByte > 0 and curByte <= 127 then
			byteCount = 1
		elseif curByte >= 192 and curByte < 223 then
			byteCount = 2
		elseif curByte >= 224 and curByte < 239 then
			byteCount = 3
		elseif curByte >= 240 and curByte <= 247 then
			byteCount = 4
		end
		i = i + byteCount
		strLen = strLen + 1
	end
	return strLen, i - 1
end

function GFunc.getTextLen2(str)
	local byteLen = #str
	local strLen = 0
	local i = 1
	local curByte
	local byteCount = 1
	while i <= byteLen do
		curByte = byte(str, i)
		byteCount = 1
		if curByte > 0 and curByte <= 127 then
			byteCount = 1
			strLen = strLen + 1
		elseif curByte >= 192 and curByte < 223 then
			-- 中文算2个字节
			byteCount = 2
			strLen = strLen + 1
		elseif curByte >= 224 and curByte < 239 then
			byteCount = 3
			strLen = strLen + 2
		elseif curByte >= 240 and curByte <= 247 then
			byteCount = 4
			strLen = strLen + 1
		end
		i = i + byteCount
	end
	return strLen, i - 1
end

-- 判断字符串是不是全部都是空格,32是半角空格，227是全角空格
function GFunc.getIsAllSpaceStr(str)
	local result = true
	local byteLen = #str
	local i = 1
	local curByte
	local byteCount = 1
	while i <= byteLen do
		curByte = byte(str, i)
		byteCount = 1
		if curByte > 0 and curByte <= 127 then
			byteCount = 1
			if curByte ~= 32 then
				result = false
				break
			end
		elseif curByte >= 192 and curByte < 223 then
			byteCount = 2
			result = false
			break
		elseif curByte >= 224 and curByte < 239 then
			byteCount = 3
			if curByte ~= 227 then
				result = false
				break
			end
		elseif curByte >= 240 and curByte <= 247 then
			byteCount = 4
			result = false
			break
		else
			result = false
			break
		end
		i = i + byteCount
	end
	return result
end

function GFunc.num2Str(count, dec)
	if count >= 99999999999 then -- 前端限制最大显示99999M
		if dec then
			return "99999.99M"
		else
			return "99999M"
		end
	elseif count >= 10000000 then
		if dec then
			count = math.floor((count*10^dec)/1000000)/(10^dec)
			return count .. "M"
		else
			count = count // 1000000
			return count .. "M"
		end
	elseif count >= 100000 then
		if dec then
			count = math.floor((count*10^dec)/1000)/(10^dec)
			return count .. "K"
		else
			count = count // 1000
			return count .. "K"
		end

	else
		return GFunc.parseDedundantNumber(count)
	end
end

-- 超过千位就缩写，目前只有公会奖励用到 1K-5K
function GFunc.num2Str2(count, dec)
	if count >= 99999999999 then -- 前端限制最大显示99999M
		if dec then
			return "99999.99M"
		else
			return "99999M"
		end
	elseif count >= 1000000 then
		if dec then
			count = math.floor((count*10^dec)/1000000)/(10^dec)
			return count .. "M"
		else
			count = count // 1000000
			return count .. "M"
		end
	elseif count >= 1000 then
		if dec then
			count = math.floor((count*10^dec)/1000)/(10^dec)
			return count .. "K"
		else
			count = count // 1000
			return count .. "K"
		end

	else
		return GFunc.parseDedundantNumber(count)
	end
end

-- 与num2Str类似 但是上限扩大3位至99999B
function GFunc.num2Str3(count, dec)
	if count >= 99999999999999 then -- 前端限制最大显示99999B
		if dec then
			return "99999.99B"
		else
			return "99999B"
		end
	elseif count >= 10000000000 then
		if dec then
			count = math.floor((count*10^dec)/1000000000)/(10^dec)
			return count .. "B"
		else
			count = count // 1000000000
			return count .. "B"
		end
	elseif count >= 10000000 then
		if dec then
			count = math.floor((count*10^dec)/1000000)/(10^dec)
			return count .. "M"
		else
			count = count // 1000000
			return count .. "M"
		end
	elseif count >= 100000 then
		if dec then
			count = math.floor((count*10^dec)/1000)/(10^dec)
			return count .. "K"
		else
			count = count // 1000
			return count .. "K"
		end

	else
		return GFunc.parseDedundantNumber(count)
	end
end

function GFunc.killDOTween(id, complete)
	if complete == nil then
		return CS.BF.Utils.KillDOTween(id)
	else
		return CS.BF.Utils.KillDOTween(id, complete)
	end
end

function GFunc.setDOTweenTimeScale(id, timeScale)
	CS.BF.Utils.SetDOTweenTimeScale(id, timeScale)
end

function GFunc.nameToLayer(name)
	return CS.BF.Utils.NameToLayer(name)
end

-- BKDRHash
function GFunc.hash(str)
	local h = 0
	for i = 1, #str do
		h = h * HASH_SEED + byte(str, i)
	end
	return h%UINT_RANGE
end

function GFunc.getScreenSize()
	return CS.UnityEngine.Screen.width, CS.UnityEngine.Screen.height
end

function GFunc.parseDedundantNumber(num)
	if num % 1 == 0 then
		return math.ceil(num)
	else
		return num
	end
end

function GFunc.openUrl(web)
	CS.UnityEngine.Application.OpenURL(web)
end

function GFunc.getShader(shaderName)
	return CS.UnityEngine.Shader.Find(shaderName)
end

function GFunc.subStringGetByteCount(str, index)
	local curByte = byte(str, index)
	local byteCount = 1
	if curByte == nil then
		byteCount = 0
	elseif curByte > 240 then
		byteCount = 4
	elseif curByte > 225 then
		byteCount = 3
	elseif curByte > 192 then
		byteCount = 2
	end
	return byteCount
end

function GFunc.subStringByCharCount(str, needCount)
	local index = 1
	local lastIndex = 1
	local num = 0
	while num < needCount and lastIndex ~= 0 do
		lastIndex = GFunc.subStringGetByteCount(str, index)
		index = index + lastIndex
		num = num + 1
	end
	return string.sub(str,1, index - 1)
end

function GFunc.setLayer(gameObject, layerName)
    local trans = gameObject.transform:GetComponentsInChildren(typeof(CS.UnityEngine.Transform))
    local layer = GFunc.nameToLayer(layerName)

    if trans then
        local len = trans.Length
        for i = 0, len - 1 do
            trans[i].gameObject.layer = layer
        end
    end
end

-- 根据当前屏幕分辨率 计算偏差值
function GFunc.calculateFitSizeY()
	local width, height = GFunc.getScreenSize()
	local dif = 0
    if width / height < DESIGN_RESOLUTION_WIDTH / DESIGN_RESOLUTION_HEIGHT then
        dif = height / (width / DESIGN_RESOLUTION_WIDTH) - DESIGN_RESOLUTION_HEIGHT
    end
	return dif
end

-- 获得Expand模式下UI分辨率
function GFunc.getUIExpandScreenSize()
	local width, height = GFunc.getScreenSize()
	if width / height <= DESIGN_RESOLUTION_WIDTH / DESIGN_RESOLUTION_HEIGHT then
		height = height * (DESIGN_RESOLUTION_WIDTH / width)
		width = DESIGN_RESOLUTION_WIDTH
	else
		width = width * (DESIGN_RESOLUTION_HEIGHT / height)
		height = DESIGN_RESOLUTION_HEIGHT
	end
	return width, height
end

function GFunc.checkNameValid(str, min, max)
	local result = nil

	if str == nil or str == "" then
		--输入不能为空
		return false
	else
		result = string.match(str, "/u{0020}") or string.match(str, "/u{3000}") or string.match(str, "/u{00A0}")
	end

	if result then
		--非法字符
		return false
	end

	local k, length = GFunc.getTextLen(str)
	if length < min then
		return false
	elseif length > max then
		return false
	end

	return true
end

function GFunc.showMessageBox(params)
	local MessageBox = require "app/ui/common/message_box"
    MessageBox:showMessageBox(params)
end

function GFunc.showToast(params)
    ModuleManager.ToastManager:showToast(params)
end

function GFunc.UiRectTransformScreenPointToLocalPointInRectangle(go, x, y)
	local uiCamera = UIManager:getUICameraComponent()
	local anchoredPosition = CS.BF.Utils.RectTransformScreenPointToLocalPointInRectangle(go:getTransform(),
		x, y, uiCamera)
	return anchoredPosition
end

function GFunc.UiRectangleContainsScreenPoint(go, x, y)
	local uiCamera = UIManager:getUICameraComponent()
	return CS.BF.Utils.RectangleContainsScreenPoint(go:getTransform(), x, y, uiCamera)
end

function GFunc.getPerByW(ratio)
	if ratio < 0 then
		return math.ceil(ratio*0.01)
	else
		return math.floor(ratio*0.01)
	end
end

function GFunc.getPerByW2(ratio)
	if ratio < 0 then
		return math.ceil(ratio*0.0001)
	else
		return math.floor(ratio*0.0001)
	end
end

function GFunc.getPerByW3(ratio)
	return ratio*0.0001
end

function GFunc.getPerStr(key, str, ispercent)
	if ispercent or key == GConst.BattleConst.BUFF_NAME.ATKP_ADD or
		key == GConst.BattleConst.BUFF_NAME.ATKP_COLOR_ADD or
		key == GConst.BattleConst.BUFF_NAME.ATKP_RED_ADD or
		key == GConst.BattleConst.BUFF_NAME.ATKP_YELLOW_ADD or
		key == GConst.BattleConst.BUFF_NAME.ATKP_GREEN_ADD or
		key == GConst.BattleConst.BUFF_NAME.ATKP_BLUE_ADD or
		key == GConst.BattleConst.BUFF_NAME.ATKP_PURPLE_ADD or
		key == GConst.BattleConst.BUFF_NAME.DEC_DMG_RED_ADD or
		key == GConst.BattleConst.BUFF_NAME.DEC_DMG_YELLOW_ADD or
		key == GConst.BattleConst.BUFF_NAME.DEC_DMG_GREEN_ADD or
		key == GConst.BattleConst.BUFF_NAME.DEC_DMG_BLUE_ADD or
		key == GConst.BattleConst.BUFF_NAME.DEC_DMG_PURPLE_ADD or
		key == GConst.BattleConst.BUFF_NAME.DEC_DMG_ALL_ADD or
		key == GConst.BattleConst.BUFF_NAME.WEAKNESS_RED_ADD or
		key == GConst.BattleConst.BUFF_NAME.WEAKNESS_YELLOW_ADD or
		key == GConst.BattleConst.BUFF_NAME.WEAKNESS_GREEN_ADD or
		key == GConst.BattleConst.BUFF_NAME.WEAKNESS_BLUE_ADD or
		key == GConst.BattleConst.BUFF_NAME.WEAKNESS_PURPLE_ADD or
		key == GConst.BattleConst.BUFF_NAME.WEAKNESS_ALL_ADD or
		key == GConst.BattleConst.BUFF_NAME.DMG_ADDITION_RED_ADD or
		key == GConst.BattleConst.BUFF_NAME.DMG_ADDITION_YELLOW_ADD or
		key == GConst.BattleConst.BUFF_NAME.DMG_ADDITION_GREEN_ADD or
		key == GConst.BattleConst.BUFF_NAME.DMG_ADDITION_BLUE_ADD or
		key == GConst.BattleConst.BUFF_NAME.DMG_ADDITION_PURPLE_ADD or
		key == GConst.BattleConst.BUFF_NAME.DMG_ADDITION_ALL_ADD or
		key == GConst.BattleConst.BUFF_NAME.HPP_ADD or
		key == GConst.BattleConst.BUFF_NAME.CRIT_TIME_ADD or
		key == GConst.BattleConst.BUFF_NAME.EXP_TIME_ADD or
		key == GConst.BattleConst.BUFF_NAME.CURED_ADD or
		key == GConst.BattleConst.BUFF_NAME.POISON or
		key == GConst.BattleConst.BUFF_NAME.SHIELD or
		key == GConst.BattleConst.BUFF_NAME.SHIELD_REBOUND_200 or
		key == GConst.BattleConst.BUFF_NAME.VULNERABLE or
		key == GConst.BattleConst.BUFF_NAME.CORRUPT or
		key == GConst.BattleConst.BUFF_NAME.BLEED or
		key == GConst.BattleConst.BUFF_NAME.WEAKEN or
		key == GConst.BattleConst.BUFF_NAME.COUNTER_ATTACK or
		key == GConst.BattleConst.BUFF_NAME.THORNS or
		key == GConst.BattleConst.BUFF_NAME.SHIELD_REBOUND_400 or
		key == GConst.BattleConst.BUFF_NAME.SHIELD_ICE_REBOUND_400 or
		key == GConst.BattleConst.BUFF_NAME.BURN or
		key == GConst.BattleConst.BUFF_NAME.SHIELD_ICE or
		key == GConst.BattleConst.BUFF_NAME.SHIELD_ICE_02 or
		key == GConst.BattleConst.BUFF_NAME.BLOCK or
		key == GConst.BattleConst.BUFF_NAME.CRIT_ADD or
		key == GConst.BattleConst.COUNTER_ATTACK or
		key == GConst.BattleConst.BUFF_NAME.SHIELD_ICE_REBOUND_200 or
		key == GConst.BattleConst.BUFF_NAME.SHIELD_REBOUND_100 or
		key == GConst.BattleConst.BUFF_NAME.SKILL_HURT_ADD_SHOW
		then
		str = str // 100 .. "%"
	end
	return str
end

function GFunc.getBuffDesc(buffName, effectNum, ispercent)
	effectNum = GFunc.getPerStr(buffName, effectNum, ispercent)
	return I18N:getTextWithOtherKey("buff", "name", buffName, "desc", effectNum)
end

function GFunc.getAttrDesc(attrName, attrNum)
	attrNum = GFunc.getAttrShowValue(attrName, attrNum, true)
	return I18N:getTextWithOtherKey("attr", "name", attrName, "desc", attrNum)
end

-- 获取属性用于显示的数值
function GFunc.getAttrShowValue(attrType, attrNum, notPercentSign)
	if table.containValue(GConst.MATCH_ATTACK_ADD_NAME, attrType) or
		table.containValue(GConst.MATCH_HP_ADD_NAME, attrType) or
		table.containValue(GConst.MATCH_NORMAL_HURTP_NAME, attrType) or
		table.containValue(GConst.MATCH_SKILL_HURTP_NAME, attrType) or
		table.containValue(GConst.MATCH_CRIT_NAME, attrType) or
		table.containValue(GConst.MATCH_CRIT_TIME_NAME, attrType) or
		table.containValue(GConst.MATCH_CURED_NAME, attrType) or
		table.containValue(GConst.MATCH_ALL_ATKP_NAME, attrType) or
		table.containValue(GConst.MATCH_ALL_HURTP_NAME, attrType)
		then
		local str = notPercentSign and "" or "%"
		return attrNum // GConst.BattleConst.PERCENT_FACTOR .. str
	else
		return attrNum // GConst.BattleConst.DEFAULT_FACTOR
	end
end

function GFunc.getAttrName(key)
	return I18N:getText("attr", key, "name")
end

-- 获取任务icon
function GFunc.getTaskIcon(taskType, taskTarget)
    if taskType == GConst.TaskConst.TASK_TYPE.X_ARENA_GRADING then
		local name = DataManager.ArenaData:getGradingIconName(DataManager.ArenaData:getGradingIdFromScore(taskTarget))
		if name == "arena_dan_1" then
			return "13"
		elseif name == "arena_dan_2" then
			return "14"
		elseif name == "arena_dan_3" then
			return "12"
		end
    else
        return ConfigManager:getConfig("task_type")[taskType].icon
    end
end

-- 获取任务描述
function GFunc.getTaskDesc(taskType, taskTarget)
    if taskType == GConst.TaskConst.TASK_TYPE.X_ARENA_GRADING then
		-- 段位显示文本
        local grading = DataManager.ArenaData:getGradingName(DataManager.ArenaData:getGradingIdFromScore(taskTarget))
        return I18N:getText("task_type", taskType, "desc", grading)
    elseif taskType == GConst.TaskConst.TASK_TYPE.X_DUNGEON_ARMOR then
		-- 防具副本显示章节和关卡
        local cfg = DataManager.DungeonData:getDungeonDataByType(ModuleManager.MODULE_KEY.DUNGEON_ARMOR):getConfig(taskTarget)
        return I18N:getText("task_type", taskType, "desc", cfg.chapter, cfg.stage)
    else
        return I18N:getText("task_type", taskType, "desc", taskTarget)
    end
end

function GFunc.getTimeStrWithMS(time)
	local m = math.floor(time/60)
	local s = time%60
	return I18N:getGlobalText(I18N.GlobalConst.TIME_MS, string.format("%02d", m), string.format("%02d", s))
end

function GFunc.getTimeStrWithMS2(time)
	local m = math.floor(time/60)
	local s = time%60
	return I18N:getGlobalText(I18N.GlobalConst.TIME_STR_MS, string.format("%02d", m), string.format("%02d", s))
end

function GFunc.getTimeStrWithHMS(time)
	local h = math.floor(time/3600)
	local m = math.floor((time%3600)/60)
	local s = time%60
	return I18N:getGlobalText(I18N.GlobalConst.TIME_HMS, string.format("%02d", h), string.format("%02d", m), string.format("%02d", s))
end

function GFunc.getTimeWithHMS(time)
	local h = math.floor(time/3600)
	local m = math.floor((time%3600)/60)
	local s = time%60
	return string.format("%02d", h), string.format("%02d", m), string.format("%02d", s)
end

function GFunc.getTimeStrWithM2(time)
	local m = time // 60
	return I18N:getGlobalText(I18N.GlobalConst.TIME_STR_M, m)
end


function GFunc.getTimeStrWithHMS2(time)
	local h = math.floor(time/3600)
	local m = math.floor((time%3600)/60)
	local s = time%60
	return I18N:getGlobalText(I18N.GlobalConst.TIME_STR_HMS, h, m, s)
end

function GFunc.getTimeStrWithDHM(time)
	local d = math.floor(time/86400)
	local h = math.floor((time%86400)/3600)
	local m = math.floor((time%3600)/60)
	return I18N:getGlobalText(I18N.GlobalConst.TIME_STR_DHM, d, h, m)
end

function GFunc.getTimeStrWithDH2(time)
	local d = math.floor(time/86400)
	local h = math.floor((time%86400)/3600)
	return I18N:getGlobalText(I18N.GlobalConst.TIME_STR_DH, d, h)
end

function GFunc.getTimeStrWithD2(time)
	local d = math.floor(time/86400)
	return I18N:getGlobalText(I18N.GlobalConst.TIME_STR_D, d)
end

function GFunc.getTimeStrWithHM(time)
	local h = math.floor(time/3600)
	local m = math.floor((time%3600)/60)
	return I18N:getGlobalText(I18N.GlobalConst.TIME_STR_HM, string.format("%02d", h), string.format("%02d", m))
end


function GFunc.getTimeStrWithHM2(time)
	local h = math.floor(time/3600)
	local m = math.floor((time%3600)/60)
	return I18N:getGlobalText(I18N.GlobalConst.TIME_STR_HM, h, m)
end

function GFunc.getTimeStr(time)
	if time > 86400 then
		return GFunc.getTimeStrWithDH2(time)
	else
		return GFunc.getTimeStrWithHMS2(time)
	end
end

function GFunc.getTimeStr2(time)
	if time > 86400 then
		return GFunc.getTimeStrWithD2(time)
	else
		return GFunc.getTimeStrWithHM2(time)
	end
end

function GFunc.getTimeStr3(time)
	if time >= 3600 then
		return GFunc.getTimeStrWithHM(time)
	else
		return GFunc.getTimeStrWithMS2(time)
	end
end

function GFunc.getAdaptScale(basePixel)
	basePixel = basePixel or 0
	local DESIGN_WIDTH = 720
	local DESIGN_HEIGHT = 1280
	local safeHeight = SafeAreaManager:getNotchScreenHeight()
	local winSizeWidth, winSizeHeight = GFunc.getScreenSize()
	local maxScale = math.max(DESIGN_WIDTH/winSizeWidth, DESIGN_HEIGHT/winSizeHeight)
	local height = maxScale*winSizeHeight - basePixel - safeHeight
	local scale = height/(DESIGN_HEIGHT - basePixel)
	return scale
end

function GFunc.getConstIntValue(key)
	local ConstCfg = ConfigManager:getConfig("const")
	return math.floor(ConstCfg[key].value)
end

function GFunc.getConstValue(key)
	local ConstCfg = ConfigManager:getConfig("const")
	return ConstCfg[key] and ConstCfg[key].value or 0
end

function GFunc.getConstReward(key)
	local ConstCfg = ConfigManager:getConfig("const")
	return ConstCfg[key] and ConstCfg[key].reward
end

function GFunc.getTargetAnchoredPosition(targetGo, parent)
	local rectTransform = targetGo:getComponent(GConst.TYPEOF_UNITY_CLASS.RECTTRANSFORM)
	local rect = rectTransform.rect
	local localToWorldMatrix = rectTransform.localToWorldMatrix
	local tarCornerMid = localToWorldMatrix:MultiplyPoint3x4(BF.Vector3((rect.xMax + rect.x) / 2, (rect.yMax + rect.y) / 2, 0))
	local screenPos = UIManager:getUICameraComponent():WorldToScreenPoint(tarCornerMid)
	local anchoredPosition = CS.BF.Utils.RectTransformScreenPointToLocalPointInRectangle(parent:getTransform(), screenPos.x, screenPos.y, UIManager:getUICameraComponent())
	return anchoredPosition
end

function GFunc.getFormatPrice(rechargeId)
	local cfg = ConfigManager:getConfig("recharge")[rechargeId]
	if cfg == nil then
		return GConst.EMPTY_STRING
	end
	-- 优先读取通过sdk获取的商品价格
	local price = SDKManager:getProductPrice(cfg.payId)
	if price and price ~= "" then
		return price
	end
	-- sdk里查不到就读表
	return cfg.price_str
end

function GFunc.setGrey(component, isGrey)
	if isGrey then
		component.color = GConst.COMPONENT_GREY_COLOR
	else
		component.color = GConst.COLOR.WHITE
	end
end

function GFunc.checkBit(value, nbit)
	if not value then
		return false
	end
	local ret = value >> (nbit - 1)
	if ret %2 == 1 then
		return true
	else
		return false
	end
end

function GFunc.showItemNotEnough(itemId)
    local gemTextInfo = I18N:getConfig("item")[itemId]
    if gemTextInfo then
        GFunc.showToast(I18N:getGlobalText(I18N.GlobalConst.ITEM_NOT_ENOUGH, gemTextInfo.name))
    end
	if itemId == GConst.ItemConst.ITEM_ID_GOLD then
		local params = {
			content = I18N:getGlobalText(I18N.GlobalConst.GOTO_COIN_STORE_DESC),
			boxType = GConst.MESSAGE_BOX_TYPE.MB_OK_CANCEL,
			okText = I18N:getGlobalText(I18N.GlobalConst.BTN_TEXT_OK),
			okFunc = function()
				UIManager:closeAllUIExceptMainUI()
				EventManager:dispatchEvent(EventManager.CUSTOM_EVENT.GO_SHOP, {page = GConst.ShopConst.PAGE_TYPE.MAIN, subType = GConst.ShopConst.MAIN_PAGE_TYPE.GOLD})
			end
		}
		GFunc.showMessageBox(params)
	end
end

function GFunc.checkCost(id, num, showToast)
	if num <= 0 then
		return true
	end
	local count = DataManager.BagData.ItemData:getItemNumById(id)
	if count < num then
		if showToast then
			GFunc.showItemNotEnough(id)
		end
		return false
	else
		return true
	end
end

---- 获得奖励的统一接口
function GFunc.addRewards(rewards, itemGetType)
	if rewards == nil then
		return
	end

	if EDITOR_MODE then
		if not itemGetType then
			local params = {
				content = "GFunc addRewards has no itemGetType",
				boxType = GConst.MESSAGE_BOX_TYPE.MB_OK,
				okText = I18N:getGlobalText(I18N.GlobalConst.BTN_TEXT_OK),
			}
			GFunc.showMessageBox(params)
			Logger.log("GFunc addRewards has no itemGetType")
		end
	end

	-- 用元表来判断此奖励是否领过了,避免重复领取
	if getmetatable(rewards) then
		return
	end
	setmetatable(rewards, EMPTY_TABLE)

	-- 合并奖励
	local newRewards = {}
	GFunc.mergeRewards2(rewards, newRewards)

	-- 根据类型type来添加奖励
	for k, v in ipairs(newRewards) do
		if v.type == GConst.REWARD_TYPE.ITEM then
			DataManager.BagData.ItemData:addItem(v.item, itemGetType)
		elseif v.type == GConst.REWARD_TYPE.EQUIP then
			DataManager.BagData.EquipData:addEquipCountById(v.equip.id, v.equip.count, itemGetType)
		end
	end
	EventManager:dispatchEvent(EventManager.CUSTOM_EVENT.GETED_REWARD_SUCCESS, newRewards)
end

function GFunc.addCosts(costs, itemGetType)
	if costs == nil then
		return
	end
	-- 用元表来判断此消耗是否领过了,避免重复扣除
	if getmetatable(costs) then
		return
	end
	setmetatable(costs, EMPTY_TABLE)
	for _, unitCost in ipairs(costs) do
		if unitCost.type == GConst.REWARD_TYPE.ITEM then
			DataManager.BagData.ItemData:addItemCost(unitCost.item, itemGetType)
		end
	end
end

-- 奖励排序 结构为后端结构
function GFunc.sortRewards(rewards)
	rewards = rewards or {}
	local maxType = 10
	for i,v in ipairs(rewards) do
		if v.type == GConst.REWARD_TYPE.ITEM then
			rewards[i].sort = (maxType - v.type) * 1000000000 + v.item.id
		elseif v.type == GConst.REWARD_TYPE.EQUIP then
			local cfg = ConfigManager:getConfig("equip")[v.equip.id]
			rewards[i].sort = (maxType - v.type) * 1000000000 + cfg.qlt*100000000 + v.equip.id
		end
	end
	table.sort(rewards, function (a, b)
		return a.sort > b.sort
	end)
end

---- 奖励展示接口
function GFunc.showRewardBox(rewards, extParams, callback)
	rewards = rewards or {}
	local params = extParams or {}
	local newRewards = {}
	if not params.noMerge then
		GFunc.mergeRewards2(rewards, newRewards, true)
	else
		newRewards = rewards
	end
	params.rewards = newRewards
	params.callback = callback
	ModuleManager.TipsManager:showRewardsBox(params)
	AudioManager:playEffect(AudioManager.EFFECT_ID.REWARD)
end

function GFunc.mergeRewards2(rewards, newRewards, needSort)
	local items = {}
	for i,v in ipairs(rewards) do
		if v.type == GConst.REWARD_TYPE.ITEM then
			items[v.item.id] = (items[v.item.id] or 0) + v.item.count
		elseif v.type == GConst.REWARD_TYPE.EQUIP then
			table.insert(newRewards, v)
		end
	end
	for k,v in pairs(items) do
		table.insert(newRewards, {type = GConst.REWARD_TYPE.ITEM, item = {id = k, count = v}})
	end
	if needSort then
		GFunc.sortRewards(newRewards)
	end
end

-- reward 结构为 type id num 配置中结构
function GFunc.mergeRewards(rewards)
	local items = {}
	local newRewards = {}
	for i,v in ipairs(rewards) do
		if v.type == GConst.REWARD_TYPE.ITEM then
			items[v.id] = (items[v.id] or 0) + v.num
		elseif v.type == GConst.REWARD_TYPE.EQUIP then
			table.insert(newRewards, v)
		elseif v.type == GConst.REWARD_TYPE.LEGACY then
			table.insert(newRewards, v)
		end
	end
	for k,v in pairs(items) do
		table.insert(newRewards, {type = GConst.REWARD_TYPE.ITEM, id = k, num = v})
	end

	return newRewards
end

function GFunc.getRandomIndex(weightArr)
	if #weightArr <= 0 then
		return
	end
    local maxWeight = 0
    for i, v in ipairs(weightArr) do
        maxWeight = maxWeight + v
    end
    local randomWeight = math.random(1, maxWeight)
    local idx
    for i, v in ipairs(weightArr) do
        if randomWeight <= v then
            idx = i
            break
        else
            randomWeight = randomWeight - v
        end
    end
    return idx
end

---- 线性随机数算法
function GFunc.getRandomIndexBySeed(seed, min, max)
    seed = (seed * 9301 + 49297)%233280
    return min + seed*(max - min + 1)//233280
end

function GFunc.getRewardsStr(rewards)
	if rewards == nil then
		return
	end

	local itemStr = ""
	for k, v in ipairs(rewards) do
		if v.type == GConst.REWARD_TYPE.ITEM then
			itemStr = itemStr .. v.type .. "." .. v.item.id .. "." .. v.item.count
		end
		if k ~= #rewards then
			itemStr = itemStr.. "|"
		end
	end
	return itemStr
end

function GFunc.getItemRewardsStr(rewards)
	if rewards == nil then
		return
	end

	local itemStr = ""
	for k, v in ipairs(rewards) do
		if v.type == GConst.REWARD_TYPE.ITEM then
			itemStr = itemStr .. v.item.id .. ":" .. v.item.count
		end
		if k ~= #rewards then
			itemStr = itemStr.. "|"
		end
	end
	return itemStr
end

function GFunc.copyStr(str)
	CS.UnityEngine.GUIUtility.systemCopyBuffer = str
end

function GFunc.getShakeSeq(obj, onlyKill, originScale, notForcerefresh)
	originScale = originScale or 1
	if obj.shakeSeq then
		if notForcerefresh then
			return obj.shakeSeq
		end
		obj.shakeSeq:Kill()
		obj.shakeSeq = nil
	end
	obj:setLocalScale(originScale, originScale, originScale)
	if onlyKill then
		return
	end
	obj.shakeSeq = obj:createBindTweenSequence()
	obj.shakeSeq:AppendInterval(2)
	obj.shakeSeq:Append(obj:getTransform():DOScale(originScale * 1.12, 0.1))
	obj.shakeSeq:Append(obj:getTransform():DOScale(originScale * 0.9, 0.11))
	obj.shakeSeq:Append(obj:getTransform():DOScale(originScale * 1.1, 0.15))
	obj.shakeSeq:Append(obj:getTransform():DOScale(originScale * 0.9, 0.2))
	obj.shakeSeq:Append(obj:getTransform():DOScale(originScale * 1.05, 0.25))
	obj.shakeSeq:Append(obj:getTransform():DOScale(originScale * 0.9, 0.3))
	obj.shakeSeq:Append(obj:getTransform():DOScale(originScale * 1, 0.3))
	obj.shakeSeq:Append(obj:getTransform():DOScale(originScale, 0.3))
	obj.shakeSeq:SetLoops(-1)
	return obj.shakeSeq
end

function GFunc.getShakeSeq2(obj, onlyKill, originScale, notForcerefresh)
	originScale = originScale or 1
	if obj.shakeSeq then
		if not notForcerefresh then
			return obj.shakeSeq
		end
		obj.shakeSeq:Kill()
		obj.shakeSeq = nil
	end
	obj:setLocalScale(originScale, originScale, originScale)
	if onlyKill then
		return
	end
	obj.shakeSeq = obj:createBindTweenSequence()
	obj.shakeSeq:AppendInterval(1)
	obj.shakeSeq:Append(obj:getTransform():DOScale(originScale * 1.12, 0.1))
	obj.shakeSeq:Append(obj:getTransform():DOScale(originScale * 0.9, 0.11))
	obj.shakeSeq:Append(obj:getTransform():DOScale(originScale * 1.1, 0.15))
	obj.shakeSeq:Append(obj:getTransform():DOScale(originScale * 0.9, 0.2))
	obj.shakeSeq:Append(obj:getTransform():DOScale(originScale * 1.05, 0.25))
	obj.shakeSeq:Append(obj:getTransform():DOScale(originScale * 0.9, 0.3))
	obj.shakeSeq:Append(obj:getTransform():DOScale(originScale * 1, 0.3))
	obj.shakeSeq:Append(obj:getTransform():DOScale(originScale, 0.3))
	obj.shakeSeq:SetLoops(-1)
	return obj.shakeSeq
end

function GFunc.getShakeSeqRotate(obj, onlyKill, notForcerefresh)
	if obj.shakeSeq then
		if notForcerefresh then
			return obj.shakeSeq
		end
		obj.shakeSeq:Kill()
		obj.shakeSeq = nil
		obj:setLocalEulerAngles(0,0,0)
	end
	if onlyKill then
		return
	end
	local objTrans = obj:getTransform()
	obj.shakeSeq = obj:createBindTweenSequence()
	obj.shakeSeq:AppendInterval(2)
	local angle = 12
	obj.shakeSeq:Append(objTrans:DORotate({x=0,y=0,z=angle},0.2))
	obj.shakeSeq:Append(objTrans:DORotate({x=0,y=0,z=-angle},0.1))
	obj.shakeSeq:Append(objTrans:DORotate({x=0,y=0,z=angle},0.1))
	obj.shakeSeq:Append(objTrans:DORotate({x=0,y=0,z=-angle},0.07))
	obj.shakeSeq:Append(objTrans:DORotate({x=0,y=0,z=angle},0.07))
	obj.shakeSeq:Append(objTrans:DORotate({x=0,y=0,z=-angle},0.05))

	obj.shakeSeq:SetLoops(-1)
	return obj.shakeSeq
end

function GFunc.getScaleQueueAni(root, rewardList, listCell, callBack)
	if root.aniSeq then
		root.aniSeq:Kill()
		root.aniSeq = nil
	end
	root.aniSeq = root:createBindTweenSequence()
	local seq = root.aniSeq
	local groupCell = {}
	local index = 1

	table.foreach(listCell, function(k, cell)
		if groupCell[index] and #groupCell[index] >= 5 then
			index = index + 1
		end

		if not groupCell[index] then
			groupCell[index] = {}
		end

		table.insert(groupCell[index], cell)
	end)

	local rewardListTrans = rewardList:getTransform()
	-- ani
	table.foreach(groupCell, function(row, cells)
		table.foreach(cells, function(k, cell)
			local summonRewardCell = cell
			local rewardType, id = summonRewardCell:getReward()
			summonRewardCell:showMask()
			local isNew = summonRewardCell:getIsFirstGet()
			local rewardQlt = GFunc.getQuality(rewardType, id)
			if rewardQlt and rewardQlt >= 4 and isNew then -- 紫色以上
				seq:AppendInterval(0.4)
				seq:AppendCallback(function() summonRewardCell:showReward()  end)
				--seq:Append(rewardListTrans:DOShakePosition(0.4, 14, 18, 90,true)) -- 轻幅度震动
				seq:Append(rewardListTrans:DOShakePosition(1, 20, 18, 90,true)) -- 大力震动
			else
				seq:AppendCallback(function()
					summonRewardCell:showReward()
				end)
			end

		end)
		seq:AppendInterval(0.1)
	end)
	seq:AppendCallback(function()
		if callBack then
			callBack()
		end
	end)

	return seq
end

function GFunc.miningTipsMovePosY(obj, delay,callBack)
	if obj.seq then
		obj.seq:Kill()
		obj.seq = nil
	end

	obj.seq = obj:createBindTweenSequence()
	local trans = obj:getTransform()
	local localPosY = trans.localPosition.y

	local seq = obj.seq
	seq:AppendInterval(delay)
	seq:Append(trans:DOLocalMoveY(localPosY + 200, 1.2))
	seq:AppendCallback(function()
		obj:setActive(false)
		if callBack then
			callBack()
		end
	end)

end

function GFunc.dataEncrypt(data)
	local numStr = CS.Security.XXTEA.Encrypt(tostring(data), GConst.SECRET_KEY)
	return numStr
end

function GFunc.dataDecrypt(data)
	local numStr = CS.Security.XXTEA.Decrypt(data, GConst.SECRET_KEY)
	return tonumber(numStr)
end

function GFunc.getTickCount()
	return math.floor(UnityTime.realtimeSinceStartup)
end

---得到展示奖励的图集名称,图片资源id
function GFunc.getFrameRes(type, id)
	if type == GConst.REWARD_TYPE.REWARD_NONE then
		return
	end

	local qlt = 1
	local atlasPath = ""
	if type == GConst.REWARD_TYPE.ITEM then
		local ItemCfg = ConfigManager:getConfig("item")

		local config = ItemCfg[id]
		if not config then
			return
		end
		qlt = config.qlt
		atlasPath = GConst.ATLAS_PATH.ICON_EQUIP
	end

	if type == GConst.REWARD_TYPE.EQUIP then
		local equipCfg = ConfigManager:getConfig("equip")

		local config = equipCfg[id]
		if not config then
			return
		end
		qlt = config.qlt
		atlasPath = GConst.ATLAS_PATH.ICON_EQUIP
	end

	if type == GConst.REWARD_TYPE.RUNES then
		local runeCfg = ConfigManager:getConfig("runes")
		local config = runeCfg[id]
		if not config then
			return
		end
		qlt = config.qlt
		atlasPath = GConst.ATLAS_PATH.ICON_RUNE
	end

	return atlasPath, GConst.HERO_FRAME[ GConst.QUALITY[qlt] ] or "frame_1", qlt
end

---得到展示奖励的图集名称,图片资源id
function GFunc.getQuality(type, id)
	if type == GConst.REWARD_TYPE.REWARD_NONE then
		return
	end

	local qlt = 1

	if type == GConst.REWARD_TYPE.ITEM then
		local ItemCfg = ConfigManager:getConfig("item")

		local config = ItemCfg[id]
		if not config then
			return
		end
		qlt = config.qlt
	end

	if type == GConst.REWARD_TYPE.EQUIP then
		local equipCfg = ConfigManager:getConfig("equip")

		local config = equipCfg[id]
		if not config then
			return
		end
		qlt = config.qlt
	end

	if type == GConst.REWARD_TYPE.LEGACY then
		local equipCfg = ConfigManager:getConfig("legacy")

		local config = equipCfg[id]
		if not config then
			return
		end
		qlt = config.qlt
	end

	if type == GConst.REWARD_TYPE.RUNES then
		local runeCfg = ConfigManager:getConfig("runes")
		local config = runeCfg[id]
		if not config then
			return
		end
		qlt = config.qlt
	end

	return qlt
end

---得到展示奖励的图集名称,图片资源id
function GFunc.getRewardIconRes(type, id)
	if type == GConst.REWARD_TYPE.REWARD_NONE then
		return
	end

	if type == GConst.REWARD_TYPE.ITEM then
		return GFunc.getIconRes(id)
	end

	if type == GConst.REWARD_TYPE.EQUIP then
		return GFunc.getEquipIconRes(id)
	end

	if type == GConst.REWARD_TYPE.RUNES then
		return GFunc.getRuneIconRes(id)
	end

	if type == GConst.REWARD_TYPE.LEGACY then
		return GFunc.getLegacyIconRes(id)
	end
end

function GFunc.getIconRes(itemId)
	local ItemCfg = ConfigManager:getConfig("item")
	local config = ItemCfg[itemId]
	if not config then
		return
	end
	return GConst.ATLAS_PATH.ICON_ITEM, tostring(config.icon)
end

function GFunc.getEquipIconRes(equipId)
	local EquipCfg = ConfigManager:getConfig("equip")
	local config = EquipCfg[equipId]
	if not config then
		return
	end
	return GConst.ATLAS_PATH.ICON_EQUIP, tostring(config.icon)
end

function GFunc.getRuneIconRes(runeId)
	local runeCfg = ConfigManager:getConfig("runes")
	local config = runeCfg[runeId]
	if not config then
		return
	end
	return GConst.ATLAS_PATH.ICON_RUNE, tostring(config.icon)
end

function GFunc.getLegacyIconRes(legacyId)
	local legacyCfg = ConfigManager:getConfig("legacy")
	local config = legacyCfg[legacyId]
	if not config then
		return
	end
	return GConst.ATLAS_PATH.ICON_LEGACY, tostring(config.icon)
end

---得到展示奖励的图集名称,图片资源id
function GFunc.getRewardName(type, id)
	if type == GConst.REWARD_TYPE.REWARD_NONE then
		return
	end

	if type == GConst.REWARD_TYPE.ITEM then
		return I18N:getText("item", id, "name")
	end

	if type == GConst.REWARD_TYPE.EQUIP then
		return I18N:getText("equip", id, "desc")
	end

	if type == GConst.REWARD_TYPE.RUNES then
		return I18N:getText("rune", id, "desc")
	end
end

function GFunc.compareVersionThan(version1, version2, include)
	if not version1 or not version2 then
		return false
	end

	local versionStrs = string.split(version1, ".")
	local versionNum1 = tonumber(versionStrs[1])
	local versionNum2 = tonumber(versionStrs[2])
	local versionNum3 = tonumber(versionStrs[3])

	local versionStrs2 = string.split(version2, ".")
	local versionNum21 = tonumber(versionStrs2[1])
	local versionNum22 = tonumber(versionStrs2[2])
	local versionNum23 = tonumber(versionStrs2[3])

	if not versionNum1 or not versionNum2 or not versionNum3 or not versionNum21 or not versionNum22 or not versionNum23 then
		return false
	end

	if versionNum1 > versionNum21 then
		return true, true
	elseif versionNum1 < versionNum21 then
		return false
	end

	if versionNum2 > versionNum22 then
		return true, true
	elseif versionNum2 < versionNum22 then
		return false
	end

	if versionNum3 > versionNum23 then
		return true
	elseif versionNum3 < versionNum23 then
		return false
	end

	if include then
		return true
	end

	return false
end

function GFunc.getFormatCfgRewards(rewards, onlyOne)
	local cfgRewards = GFunc.getArray()
	for _, reward in ipairs(rewards) do
		local cfgReward = {
			type = reward.type,
			id = reward.id,
			count = {
				value = reward.count.value,
				unit = reward.count.unit,
			},
		}
		if onlyOne then
			return cfgReward
		else
			table.insert(cfgRewards, reward)
		end
	end

	return cfgRewards
end

function GFunc.getUpdateDataMap(fnName, dataName, dataMap, costs, rewards, fnArgs)
	local params = {
		fnName = fnName,
		dataType = dataName,
		data = dataMap,
		costs = costs or GFunc.getArray(),
		rewards = rewards or GFunc.getArray(),
		fnArgs = fnArgs
	}
	return params
end

function GFunc.isShenhe()
	return CS.BF.BFMain.IsShenhe
end

function GFunc.isInWhitelist()
	return CS.BF.BFMain.IsWhite
end

function GFunc.getArray()
	local array = {}
	setmetatable(array, {__jsontype = "array"})
	return array
end

function GFunc.getTable(struct)
    local t = {}
    if struct then
        for k, v in pairs(struct) do
            if type(v) == "table" then
                t[k] = GFunc.getTable(v)
            else
                t[k] = v
            end
        end
    end

    return t
end

function GFunc.formatTimeStep(timestep)
	if timestep and timestep > 10000000000 then
		timestep = timestep // 1000
	end
	return timestep
end

function GFunc.randomPos(idx, pos)
	local posX, posY
	if idx == 1 then
		posX = math.random(-50, 0)
		posY = math.random(0, 50)
	elseif idx == 2 then
		posX = math.random(0, 50)
		posY = math.random(0, 50)
	elseif idx == 3 then
		posX = math.random(-50, 0)
		posY = math.random(-50, 0)
	elseif idx == 4 then
		posX = math.random(0, 50)
		posY = math.random(-50, 0)
	else
		posX = math.random(-50, 50)
		posY = math.random(-50, 50)
	end
	return posX + pos.x, posY + pos.y
end

function GFunc.imgFly(flyImg, beginPos, endPos, callback)
	if flyImg.aniSeq then
		flyImg.aniSeq:Kill()
	end
	local scale = 0.5
	flyImg:setAnchoredPosition(beginPos.x, beginPos.y)
	flyImg:setVisible(true, 0.1)

	local maxDis = 2000
	local dis = BF.Vector2Distance(beginPos, endPos)
	local time = dis/maxDis
	local aniSeq = flyImg:createBindTweenSequence()
	-- aniSeq:Append(flyImg:getTransform():DOScale(BF.Vector3(scale + 0.2, scale + 0.2, scale + 0.2), 0.1):SetEase(CS.DG.Tweening.Ease.InCubic))
	aniSeq:Append(flyImg:getTransform():DOScale(BF.Vector3(scale, scale, scale), 0.04):SetEase(CS.DG.Tweening.Ease.InCubic))
	-- aniSeq:AppendInterval(0.3)
	aniSeq:Append(flyImg:getTransform():DOAnchorPos(endPos, time):SetEase(CS.DG.Tweening.Ease.InCubic))
	aniSeq:Join(flyImg:getTransform():DOScale(BF.Vector3(0.1, 0.1, 0.1), time):SetEase(CS.DG.Tweening.Ease.InCubic))
	aniSeq:AppendCallback(function()
		flyImg:setVisible(false)
		if callback then
			callback()
		end
	end)
	flyImg.aniSeq = aniSeq
end

function GFunc.doScaleFlyImg(img, callback)
	if img.aniSeq then
		img.aniSeq:Kill()
		img:setLocalScale(1, 1, 1)
	end
	local transform = img:getTransform()
	local localScale = transform.localScale
	local scale = localScale.x

	local aniSeq = img:createBindTweenSequence()
	aniSeq:Append(img:getTransform():DOScale(BF.Vector3(scale + 0.1, scale + 0.1, scale + 0.1), 0.08):SetEase(CS.DG.Tweening.Ease.InCubic))
	aniSeq:Append(img:getTransform():DOScale(BF.Vector3(scale - 0.08, scale - 0.08, scale - 0.08), 0.08):SetEase(CS.DG.Tweening.Ease.InCubic))
	aniSeq:Append(img:getTransform():DOScale(BF.Vector3(scale, scale, scale), 0.08):SetEase(CS.DG.Tweening.Ease.InCubic))
	aniSeq:AppendCallback(function()
		if callback then
			callback()
		end
	end)
	img.aniSeq = aniSeq
end

function GFunc.doScaleQuickZoom(img,callback)
	if img.aniSeq then
		img.aniSeq:Kill()
	end
	local transform = img:getTransform()
	local aniSeq = img:createBindTweenSequence()
	aniSeq:Append(transform:DOScale(1.4, 0.1))
	aniSeq:Append(transform:DOScale(1, 0.06))
	aniSeq:AppendCallback(function()
		if callback then
			callback()
		end
	end)
	img.aniSeq = aniSeq
end

-- colorType 1 白色， 2 黑色
function GFunc.setAdsSprite(img, isGrey)
	if not img then
		return
	end
	local skip = false -- DataManager.MallActData:skipAd()
	local icon = nil
	if isGrey then
		icon = skip and "common_ad_6" or "common_ad_5"
	else
		icon = skip and "common_ad_4" or "common_ad_3"
	end
	img:setSprite(GConst.ATLAS_PATH.COMMON, icon)
end

function GFunc.getAdSprite()
	local skip = false --DataManager.MallActData:skipAd()
	return skip and "common_ad_4" or "common_ad_3"
end

function GFunc.centerImgAndTx(imgObj, txObj, spacing, offset, txMaxWidth)
    spacing = spacing or 0
	offset = offset or 0
    local imgW = imgObj:getSizeDelta().x
    local txW = txObj:getComponent(GConst.TYPEOF_UNITY_CLASS.UI_TEXT_MESH_PRO).preferredWidth
	-- 文字最大宽度处理
	if txMaxWidth and txW > txMaxWidth then
		txW = txMaxWidth
	end
	txObj:setSizeDeltaX(txW)
    local w = (imgW + txW + spacing) / 2
    imgObj:setAnchoredPositionX(imgW / 2 - w + offset)
    txObj:setAnchoredPositionX(w - txW / 2 + offset)
end

-- centerImgAndTx的位置反向版本
function GFunc.centerTxAndImg(txObj, imgObj, spacing, offset)
    spacing = spacing or 0
	offset = offset or 0
    local imgW = imgObj:getSizeDelta().x
    local txW = txObj:getComponent(GConst.TYPEOF_UNITY_CLASS.UI_TEXT_MESH_PRO).preferredWidth
	txObj:setSizeDeltaX(txW)
    local w = (imgW + txW + spacing) / 2
    txObj:setAnchoredPositionX(txW / 2 - w + offset)
    imgObj:setAnchoredPositionX(w - imgW / 2 + offset)
end

function GFunc.centerTxAndTx(txObj1, txObj2, spacing)
    spacing = spacing or 0
    local txW = txObj1:getComponent(GConst.TYPEOF_UNITY_CLASS.UI_TEXT_MESH_PRO).preferredWidth
	txObj1:setSizeDeltaX(txW)

	local txW2 = txObj2:getComponent(GConst.TYPEOF_UNITY_CLASS.UI_TEXT_MESH_PRO).preferredWidth
	txObj2:setSizeDeltaX(txW2)

    local w = (txW2 + txW + spacing) / 2
    txObj1:setAnchoredPositionX(txW / 2 - w)
    txObj2:setAnchoredPositionX(w - txW2 / 2)
end

function GFunc.centerTxAndImgAndTx(txObj1, spacing1, imgObj, spacing2, txObj2)
    local txW = txObj1:getComponent(GConst.TYPEOF_UNITY_CLASS.UI_TEXT_MESH_PRO).preferredWidth
	txObj1:setSizeDeltaX(txW)

	local txW2 = txObj2:getComponent(GConst.TYPEOF_UNITY_CLASS.UI_TEXT_MESH_PRO).preferredWidth
	txObj2:setSizeDeltaX(txW2)

	local imgW = imgObj:getSizeDelta().x
    local w = (txW + spacing1 + imgW + spacing2 + txW2) / 2
    txObj1:setAnchoredPositionX(txW / 2 - w)
	imgObj:setAnchoredPositionX(txW - w + spacing1 + imgW / 2)
    txObj2:setAnchoredPositionX(txW - w + spacing1 + imgW + spacing2 + txW2 / 2)
end

function GFunc.expandImgToFitTx(imgObj, txObj, spacing)
	spacing = spacing or 0
    local txW = txObj:getComponent(GConst.TYPEOF_UNITY_CLASS.UI_TEXT_MESH_PRO).preferredWidth
	txObj:setSizeDeltaX(txW)
    imgObj:setSizeDeltaX(txW + spacing * 2)
end

function GFunc.formatAdImg(adImg)
    if DataManager.MonthlyData:skipAd() then
        adImg:setSprite(GConst.ATLAS_PATH.COMMON, "common_ad_1")
    else
        adImg:setSprite(GConst.ATLAS_PATH.COMMON, "common_ad")
    end
end

function GFunc.organizeCfg(cfgId, cfg)
	local result = {}
	result.cfgId = cfgId
	result.cfg = cfg
	return result
end

function GFunc.gemEnough(needNum,showToast) -- 钻石是否足够
	return GFunc.checkCost(GConst.ItemConst.ITEM_ID_GEM, needNum, showToast) --判断不提示，点击才提示
end

---设置图片和文本居中, 文本需要左右居中对齐
function GFunc.setMiddlePosX(txtObj, icon, bgImg)
	local txtRectWidth = txtObj:getTransform().rect.width
	local iconRectWidth = icon:getTransform().rect.width
	local bgImgRectWidth = bgImg:getTransform().rect.width
	local halfBgWidth = bgImgRectWidth * 0.5
	-- 文本的宽度
	local meshPro = txtObj:getComponent(GConst.TYPEOF_UNITY_CLASS.UI_TEXT_MESH_PRO)
	local width = meshPro.preferredWidth
	local txtMinWidth = math.max(width, txtRectWidth)

	-- 以bg中心点为锚点时
	-- 文本距离边界的距离
	local txtPadding = halfBgWidth - txtMinWidth
	txtPadding = txtPadding > 0 and txtPadding or 0
	-- 图片距离边界距离
	local iconPadding = halfBgWidth - iconRectWidth
	iconPadding = iconPadding > 0 and iconPadding or 0

	local maxPadding = math.max(txtPadding, iconPadding)
	local minPadding = math.min(txtPadding, iconPadding)
	local offset = (maxPadding - minPadding) * 0.5

	if txtPadding > iconPadding then
		txtObj:setAnchoredPositionX(txtMinWidth * 0.5 + offset)
		icon:setAnchoredPositionX(-iconRectWidth * 0.5 + offset)
	else
		txtObj:setAnchoredPositionX(txtMinWidth * 0.5 - offset)
		icon:setAnchoredPositionX(-iconRectWidth * 0.5 - offset)
	end
end

---计算最小值
function GFunc.getCalcMinVal(arr)
	local min = arr[1]
	for i, v in ipairs(arr) do
		if min > v then
			min = v
		end
	end
	return min
end

---计算最大值
function GFunc.getCalcMaxVal(arr)
	local max = arr[1] or 0
	for i, v in ipairs(arr) do
		if max < v then
			max = v
		end
	end
	return max
end

function GFunc.getRewardTableByRewardList(rewardList)
	local newRewards = {}
	for _, reward in ipairs(rewardList) do
		table.insert(newRewards, reward)
	end

	return newRewards
end

function GFunc.getRewardTableByReward(reward)
	return GFunc.getRewardTable(reward.type, reward.id, reward.count)
end

function GFunc.getRewardTable(type, id, count)
	if type == GConst.REWARD_TYPE.EQUIP then
		return GFunc.getServerEquipRewardTable(id, count)
	end
	return {
		type = type,
		id = id,
		count = count
	}
end

function GFunc.formatRewardsToServerStruct(rewardList)
	local rewards = {}
	for _, reward in ipairs(rewardList) do
		local serverReward =  GFunc.getServerRewardTable(reward.type, reward.id, reward.count)
		table.insert(rewards, serverReward)
	end
	return rewards
end

function GFunc.getServerRewardTable(type, id, count)
	if type == GConst.REWARD_TYPE.ITEM then
		return GFunc.getServerItemRewardTable(id, count)
	end
end

function GFunc.getServerItemRewardTable(id, count)
	local reward = {
		type = GConst.REWARD_TYPE.ITEM,
		item = {
			id = id,
			count = count
		},
	}
	return reward
end

function GFunc.getServerRuneRewardTable(id, level)
	return {
		type = GConst.REWARD_TYPE.RUNES,
		rune = {
			id = id,
			level = level,
		},
	}
end

function GFunc.getServerEquipRewardTable(id, level, count)
	local reward = {
		type = GConst.REWARD_TYPE.EQUIP,
		equip = {
			id = id,
			level = level,
			count = count
		},
	}
	return reward
end

function GFunc.checkTableValueSame(standardTable, compareTable, debug)
	if type(standardTable) ~= "table" or type(compareTable) ~= "table" then
		if debug and standardTable ~= compareTable then
			Logger.logHighlight(standardTable)
		end
		return standardTable == compareTable
	end

	for k, v in pairs(standardTable) do
		local v2 = compareTable[k]
		if v2 == nil then
			if debug then
				Logger.logHighlight(k)
			end
			return false
		end

		if not GFunc.checkTableValueSame(v, v2, debug) then
			return false
		end
	end

	return true
end

function GFunc.showCurrencyFlyAction(rewards, pos)
	local flyPos = {}
	if not pos then
		pos = {x = 0, y = 0}
	else
		local sPoint = UIManager:getUICameraComponent():WorldToScreenPoint(pos)
		pos = CS.BF.Utils.RectTransformScreenPointToLocalPointInRectangle(UIManager:getMainCanvasTransform(), sPoint.x, sPoint.y, UIManager:getUICameraComponent())
	end
	for i, reward in ipairs(rewards) do
		if reward.type == GConst.REWARD_TYPE.ITEM then
			local allPos = {}
			for i = 1, 4 do
				local posX, posY = GFunc.randomPos(i, pos)
				allPos[i] = {x = posX, y = posY}
			end
			flyPos[reward.item.id] = allPos
		end
	end

	UIManager:showCurrencyAction(flyPos)
end

function GFunc.getSkillEffectStr(id, lv)
	local str = I18N:getText("skill", id, "desc")
	local cfg = ConfigManager:getConfig("skill")[id]
	local baseNum = cfg.string_num_base
	local growNum = cfg.string_num_grow
	local tab = {}
	for i,v in ipairs(baseNum) do
		table.insert(tab, baseNum[i] + (lv - 1)*growNum[i])
	end
	local idx = 0
	str = string.gsub(str, '{%d+}', function (s)
		idx = idx + 1
		return tostring(tab[idx])
	end)

	return str
end

function GFunc.goTargetElasticityY(obj, onlyKill, offset, punchY, callBack)
	if obj.shakeSeq then
		obj.shakeSeq:Kill()
		obj.shakeSeq = nil
	end
	if onlyKill then
		return
	end
	local curPosY = obj:getAnchoredPositionY()
	local objTrans = obj:getTransform()
	obj.shakeSeq = obj:createBindTweenSequence()
	obj.shakeSeq:Append(objTrans:DOLocalMoveY(curPosY - offset / 2,0.1))
	obj.shakeSeq:Append(objTrans:DOLocalMoveY(curPosY + offset * 2 ,0.1))
	obj.shakeSeq:Join(objTrans:DOPunchScale({x=0,y=punchY,z = 0},0.2, 6))
	obj.shakeSeq:AppendCallback(function()
		if callBack then
			callBack()
		end
	end)
	return obj.shakeSeq
end

function GFunc.goTargetPosXShake(obj, onlyKill, targetX, offset, callBack)
	if obj.shakeSeq then
		obj.shakeSeq:Kill()
		obj.shakeSeq = nil
	end
	if onlyKill then
		return
	end
	obj.shakeSeq = obj:createBindTweenSequence()
	local objTrans = obj:getTransform()
	local seq = obj.shakeSeq
	seq:Append(objTrans:DOLocalMoveX(targetX,0.12))
	seq:Append(objTrans:DOLocalMoveX(targetX + offset,0.1))
	seq:Append(objTrans:DOLocalMoveX(targetX - offset / 3,0.05))
	seq:Append(objTrans:DOLocalMoveX(targetX, 0.1))
	seq:AppendCallback(function()
		if callBack then
			callBack()
		end
	end)
	return obj.shakeSeq
end

function GFunc.getRewardId(reward)
	if reward.id_for_nothing then
		return CS.BF.Utils.GetRewardNumber(reward.id_for_nothing)
	else
		return reward.id
	end
end

function GFunc.getRewardNum(reward)
	if reward.num_for_nothing then
		return CS.BF.Utils.GetRewardNumber(reward.num_for_nothing)
	else
		return reward.num or reward.count
	end
end

function GFunc.getRewardType(reward)
	if reward.type_for_nothing then
		return CS.BF.Utils.GetRewardNumber(reward.type_for_nothing)
	else
		return reward.type
	end
end

function GFunc.DOBFSliderValue(slider, endValue, duration, snapping)
	return CS.BF.Utils.DOBFSliderValue(slider, endValue, duration, snapping)
end

function GFunc.intToString(int)
	return GConst.INT_TO_STRING[int] or tostring(int)
end

function GFunc.getTempList()
	for i = 1, #TEMP_LIST do
		table.remove(TEMP_LIST)
	end
	return TEMP_LIST
end

function GFunc.recycleTempList()
	for i = 1, #TEMP_LIST do
		table.remove(TEMP_LIST)
	end
end

function GFunc.getTempMap()
	for k, v in pairs(TEMP_MAP) do
		TEMP_MAP[k] = nil
	end
	return TEMP_MAP
end

function GFunc.recycleTempMap()
	for k, v in pairs(TEMP_MAP) do
		TEMP_MAP[k] = nil
	end
end


-- 设置tabLe只速 出现改写会抛出Lua error
-- 用法locaL readOnlyCfg = GFunc.readOnlyTab(cfg) return readOnlyCfg
-- 增加了防重置设置read_onLy的机制
-- Lua5.3支持 1) table库支持调用元方法，所以table.remove table.insert也会抛出错误,
-- 			 2)不用定义_ipairs 5.3 ipairs迭代器支持访问元方法__index，pairs迭代器next不支持故需要元方法_pairs
-- 低版本Lua此网数不能完全按照预期工作
function GFunc.readOnlyTab(inputTable)
	local travelled_tables = {}
	local function _read_only(tbl)
		if not travelled_tables[tbl] then
			local tbl_mt = getmetatable(tbl)
			if not tbl_mt then
				tbl_mt = {}
				setmetatable(tbl, tbl_mt)
			end
			local proxy = tbl_mt._read_only__proxy
			if not proxy then
				proxy = {}
				tbl_mt.__read_only_proxy = proxy
				local proxy_mt = {
					__index = tbl,
					__newindex = function (t, k, v) error( "error write to a read-only table with key = " .. tostring(k)) end,
					__pairs = function (t) return pairs(tbl) end,
					-- __ipairs = function (t) return ipairs(tbl) end, 5.3版本不需要此方法
					_len = function (t) return #tbl end,
					__read_only_proxy = proxy
				}
				setmetatable(proxy, proxy_mt)
			end
			travelled_tables[tbl] = proxy
			for k, v in pairs(tbl) do
				if type(v) == "table" then
					tbl[k] = _read_only(v)
				end
			end
		end
		return travelled_tables[tbl]
	end
	return _read_only(inputTable)
end

function GFunc.encryptNumber(value)
	-- return value + GConst.NUMBER_ENCRYPTION_CONSTANT
	return value
end

function GFunc.decryptNumber(value)
	-- return value - GConst.NUMBER_ENCRYPTION_CONSTANT
	return value
end

function GFunc.formatPlayerName(name)
    if name == nil or #name == 0 then
        name = I18N:getGlobalText(I18N.GlobalConst.NEW_PLAYER)
    end
	return name
end

-- info = {array_heroes, heroes_equips, skins, heroes_runes} -- 服务器返回的数据
function GFunc.formatPlayerFormationInfo(info)
	local formation = {}
	if not info.array_heroes then
		return formation
	end
	for index, info in ipairs(info.array_heroes) do
		if DataManager.HeroData:isExistHeroById(info.id) then
			local heroEntity = DataManager.HeroData:getEntity(info)
			formation[heroEntity:getMatchType()] = heroEntity
		end
	end

	if not info.heroes_equips then
		info.heroes_equips = {}
	end
	if not info.skins then
		info.skins = {}
	end
	if not info.heroes_runes then
		info.heroes_runes = {}
	end

	for matchType, heroEntity in pairs(formation) do
		local heroId = heroEntity:getCfgId()

		-- 处理装备
		local equipInfo = info.heroes_equips[heroId]
		if not equipInfo then
			equipInfo = {equips = {}}
		end
		for partName, partType in pairs(GConst.EquipConst.PART_TYPE) do
			local level = equipInfo.equips[partType] or 0
			local equipEntity = DataManager.EquipData:createEntity(heroId, partType, level)

			equipEntity:setHeroEntity(heroEntity)
			heroEntity:setEquips(partType, equipEntity)
		end

		-- 处理皮肤
		local skinIds = {}
		for idx, skinId in pairs(info.skins) do
			if DataManager.SkinData:getOriginSkinId(heroId) ~= skinId and DataManager.SkinData:getHeroIdBySkinId(skinId) == heroId then
				table.insert(skinIds, skinId)
			end
		end
		if #skinIds > 0 then
			heroEntity:setSkins(skinIds)
		end

		-- 处理符文
		if info.heroes_runes[heroId] == nil then
			info.heroes_runes[heroId] = {grids = {}}
		end
		local runesEntity = DataManager.RunesData:createEntity(heroId, info.heroes_runes[heroId].grids)
		heroEntity:setRunes(runesEntity)
	end

	return formation
end

function GFunc.randomDrop(dropList)
	local weightArr = {}
	for i, v in ipairs(dropList) do
		weightArr[i] = v.weight
	end
	local idx = GFunc.getRandomIndex(weightArr)
	return dropList[idx]
end

-- 获取属性描述和数值
function GFunc.getStarImg(starType)
	if starType == GConst.HeroConst.STAR_TYPE.LV_1 then
		return GConst.ATLAS_PATH.COMMON, "common_star_1"
	elseif starType == GConst.HeroConst.STAR_TYPE.LV_2 then
		return GConst.ATLAS_PATH.COMMON, "common_point"
	else
		return GConst.ATLAS_PATH.COMMON, "common_info"
	end
end

-- 获取计算了加成的最终属性值（全部属性，固定属性id，加成属性id）
function GFunc.getFinalAttrValue(allAttrs, fixedId, factorId)
    local fixedValue = allAttrs[fixedId] or 0
    local factorValue = allAttrs[factorId] or 0
    if factorValue > 0 then
        return math.floor(fixedValue * (1 + factorValue / GConst.DEFAULT_FACTOR) + 0.0000001)
    else
        return fixedValue
    end
end

return GFunc