local ArenaData = class("ArenaData", BaseData)

-- 竞技场

function ArenaData:ctor()
    self.data.isDirty = false
end

function ArenaData:clear()
    self.lastArenaGiftId = nil
    self.curArenaGiftId = nil
    self.giftExpireAt = nil
end

function ArenaData:init(data)
    data = data or GConst.EMPTY_TABLE

    self.season = data.season_number
    self.score = data.current_score
    self.lastScore = data.last_score
    self.lastChallengeCount = data.last_challenge_count-- 上赛季挑战次数
    self.seasonRewardGot = data.settle_reward_season-- 最近领取过奖励的赛季
    self.todayMatchCount = data.today_match_count
    self.matchInfo = data.rival_info
    self.matchSuccessTime = data.match_success_at // 1000-- 匹配成功时间，超过配置时间后，匹配对象过期，需要重新匹配
    self.matchFreeCdEndTime = data.match_success_cd_end_at // 1000-- 免费匹配cd结束时间，<=0表示无cd，>0表示cd剩余时间
    self.todayAdRematchCount = data.today_ad_count-- 今日看广告跳匹配cd次数
    self.todayWinAdBoxCount = data.ad_box_win_reward_count-- 今日胜利宝箱次数
    self.todayLoseAdBoxCount = data.ad_box_lose_reward_count-- 今日失败宝箱次数
    self.maxScore = data.highest_score_in_history-- 历史最高积分
    self.gotGradingRewardIds = data.stage_gift_id-- 已领取的段位奖励id
    self:updateTotalFightCount(data)
    -- 初始化rank配置
    self.cfgRank = self:getRankCfg(self.season)
    -- 初始化time配置
    self.cfgTime = self:getTimeCfg(self.season)

    self.curGradingId = self:getGradingIdFromScore(self.score) -- 当前所在段位id
    DataManager.FormationData:initArena(data.attack_array_heroes, data.defend_array_heroes)
    DataManager.PlayerData:initArena(data.today_ticket_buy_count, data.today_ticket_ad_count)

    -- 未找到相应赛季数据
    if not self:hasServerData() then
        GFunc.showToast(I18N:getGlobalText(I18N.GlobalConst.ARENA_DESC_30))
    end

    DataManager:registerCrossDayFunc("ArenaData", function()
		self:onDayChanged()
    end)

    self:setDirty()
end

function ArenaData:initGiftInfo(actArenaGift, isInit)
    if not actArenaGift then
        return
    end
    if self.lastArenaGiftId ~= actArenaGift.current_gift_id and not isInit then
        self.lastArenaGiftId = nil
    else
        self.lastArenaGiftId = actArenaGift.current_gift_id
    end
    self.curArenaGiftId = actArenaGift.current_gift_id
    self.giftExpireAt = GFunc.formatTimeStep(actArenaGift.gift_expire_at)
end

function ArenaData:needShowPopGift()
    return self.lastArenaGiftId ~= self.curArenaGiftId
end

function ArenaData:cleaShowPopGift()
    self.lastArenaGiftId = self.curArenaGiftId
end

function ArenaData:getGiftId()
    if GFunc.isShenhe() then -- 审核状态不显示
        return
    end
    if not self.curArenaGiftId or self.curArenaGiftId <= 0 then
        return
    end
    if self:getGiftRemainTime() <= 0 then
        return
    end
    return self.curArenaGiftId
end

function ArenaData:getGiftRemainTime()
    if not self.giftExpireAt then
        return 0
    end
    return self.giftExpireAt - Time:getServerTime()
end

function ArenaData:onBoughtGift(giftId)
    if giftId ~= self.curArenaGiftId then
        return
    end
    self.lastArenaGiftId = nil
    self.curArenaGiftId = nil
    self.giftExpireAt = nil
end

function ArenaData:setDirty()
    self.data.isDirty = not self.data.isDirty 
end

-- 通用 ----------------------------------------------------------------------

-- 获取赛季rank配置
function ArenaData:getRankCfg(season)
    local result = {}
    for id, data in pairs(ConfigManager:getConfig("arena_rank")) do
        if data.season == season then
            result[id] = data
        end
    end
    return result
end

-- 获取赛季time配置
function ArenaData:getTimeCfg(season)
    local result = table.find(ConfigManager:getConfig("arena_time"), function(value)
        return value.season == season
    end)
    return result
end

-- 是否开启，checkData：是否判断数据
function ArenaData:isOpen(checkData)
    if not ModuleManager:getIsOpen(ModuleManager.MODULE_KEY.ARENA, true) then
        return false
    end
    if checkData and not self:hasServerData() then
        return false
    end
    return true
end

-- 是否有服务器数据
function  ArenaData:hasServerData()
    return self.season and self:getRankCfg(self.season)
end

-- 是否在赛季结算中
function ArenaData:isInSeasonSettlementTime()
    return self:getRemainSeasonTime() > 0 and self:getRemainSeasonSettlementTime() < 0
end

-- 是否在赛季时间段中
function ArenaData:isInSeasonTime()
    return self:getRemainSeasonTime() > 0 and self:getRemainSeasonSettlementTime() > 0
end

-- 获取当前为第几赛季
function ArenaData:getSeason()
    return self.season
end

-- 获取当前赛季段位id列表(升序)
function ArenaData:getGradingIdList()
    local result = table.keys(self.cfgRank)
    table.sort(result)
    return result
end

-- 获取赛季剩余时间（秒）
function ArenaData:getRemainSeasonTime()
    local endTime = Time:getCertainTimeByStr(self.cfgTime.end_time)
    return endTime - Time:getServerTime()
end

-- 获取赛季结算的剩余时间（秒）
function ArenaData:getRemainSeasonSettlementTime()
    -- 赛季结束前30分钟结算
    local lockTime = Time:getCertainTimeByStr(self.cfgTime.end_time) - 30 * 60
    return lockTime - Time:getServerTime()
end

-- 获取赛季段位结算奖励信息
function ArenaData:getSeasonRewards(id)
    local cfg = ConfigManager:getConfig("arena_rank")
    if cfg[id] == nil then
        Logger.logError("未找到段位[%d]相关数据", id)
        return nil
    end
    return cfg[id].season_reward
end

-- 获取当前赛季最低段位积分
function ArenaData:getSeasonGradingMinScore()
    local result = 0
    if self.cfgRank then
        for id, data in pairs(self.cfgRank) do
            if result == 0 or result > data.score then
                result = data.score
            end
        end
    end
    return result
end

-- 获取积分所对应的段位id
function ArenaData:getGradingIdFromScore(score, isLastSeason)
    if score == nil or score < 0 then
        return nil
    end

    local gradingId,gradingInfo = nil
    local cfg = self.cfgRank
    if isLastSeason then
        cfg = self:getRankCfg(self.season - 1)
    end
    if cfg == nil then
        -- 活动没开特殊处理
        cfg = self:getRankCfg(1)
    end

    local ids = table.keys(cfg)
    table.sort(ids)
    for index, id in ipairs(ids) do
        if score >= cfg[id].score and (gradingInfo == nil or gradingInfo.score < cfg[id].score) then
            gradingId, gradingInfo = id, cfg[id]
        end
    end

    return gradingId
end

-- 获取所在段位的积分 cur - min
function ArenaData:getGradingScore(score)
    return score - self:getGradingMinScore(score)
end

-- 获取所在段位的最低积分
function ArenaData:getGradingMinScore(score)
    local id = self:getGradingIdFromScore(score)
    if not self.cfgRank[id] then
        id = table.sort(table.keys(self.cfgRank))
    end
    return self.cfgRank[id].score
end

-- 获取所在段位的最高积分 full
function ArenaData:getGradingMaxScore(score)
    local gradingId = self:getGradingIdFromScore(score)
    local ids = self:getGradingIdList()
    local curIdx = table.indexof(ids, gradingId)
    if curIdx and curIdx + 1 <= #ids then
        return self.cfgRank[ids[curIdx + 1]].score
    end
end

-- 获取所在段位的总积分
function ArenaData:getGradingScoreTotal(score)
    local max = self:getGradingMaxScore(score)
    local min = self:getGradingMinScore(score)
    if max and min then
        return max - min
    else
        return nil
    end
end

-- 获取段位图标名（大段位区分）
function ArenaData:getGradingIconName(id)
    local cfg = ConfigManager:getConfig("arena_rank")
    if cfg[id] == nil then
        Logger.logError("未找到段位[%d]相关数据", id)
        return nil
    end
    return cfg[id].rank_icon
end

-- 获取段位小段图标（大段中的小段区分）
function ArenaData:getGradingNumName(id)
    local cfg = ConfigManager:getConfig("arena_rank")
    if cfg[id] == nil then
        Logger.logError("未找到段位[%d]相关数据", id)
        return nil
    end
    return cfg[id].rank_show
end

-- 获取段位名称
function ArenaData:getGradingName(id)
    local cfg = ConfigManager:getConfig("arena_rank")
    if id == nil or cfg[id] == nil then
        Logger.logError("未找到段位[%d]相关数据", id)
        return nil
    end
    return I18N:getGlobalText(cfg[id].name_txt)
end

-- 获取重新刷新匹配时间（秒）
function ArenaData:getRematchRefreshTime()
    return GFunc.getConstIntValue("arena_refresh_time") * 60
end

-- 获取重新匹配每天最大广告次数
function ArenaData:getRematchAdDailyMaxNum()
    return GFunc.getConstIntValue("arena_refresh_ad_times")
end

-- 获取重新匹配钻石消耗
function ArenaData:getRematchConstGem()
    return GFunc.getConstIntValue("arena_refresh_cost")
end

-- 获取匹配信息最多存在时间（秒）
function ArenaData:getMatchInfoMaxTime()
    return GFunc.getConstIntValue("arena_info_time") * 60 * 60
end

-- 获取战斗消耗
function ArenaData:getFightCostNum()
    return GFunc.getRewardNum(GFunc.getConstReward("arena_fight_cost"))
end

-- 入场券是否足够
function ArenaData:isEnoughTicket()
    return self:getFightCostNum() <= DataManager.BagData.ItemData:getItemNumById(GConst.ItemConst.ITEM_ID_ARENA_TICKET)
end

-- 排行榜 ----------------------------------------------------------------------

-- 排行榜玩家总数
function ArenaData:getRankCount()
    if self.rankList then
        return #self.rankList
    else
        return 0
    end
end

-- 获取排行榜第n名数据
function ArenaData:getRankData(rank)
    local info = table.find(self.rankList, function (value)
        return value.rank == rank
    end)

    if info == nil then
        Logger.logError("没有找到排行榜第%d名数据", rank)
    end

    return info
end

-- 本地是否有排行榜该玩家编队数据
function ArenaData:hasRankFormation(id)
    return self.rankFormation ~= nil and self.rankFormation[id] ~= nil
end

-- 获取排行榜玩家编队信息
function ArenaData:getRankFormation(id)
    return self.rankFormation[id]
end

-- 匹配 ----------------------------------------------------------------------

-- 获取匹配玩家信息
function ArenaData:getMatchInfo()
    if EDITOR_MODE then
        Logger.logHighlight("当前匹配玩家信息")
        Logger.printTable(self.matchInfo)
    end

    if Time:getServerTime() - self.matchSuccessTime > self:getMatchInfoMaxTime() then
        -- 超过时间需要重新匹配
        return nil
    end
    if self.matchInfo == nil then
        return nil
    end

    return self.matchInfo
end

-- 匹配玩家剩余存在时间
function ArenaData:getMatchExistRemainTime()
    return self:getMatchInfoMaxTime() - (Time:getServerTime() - self.matchSuccessTime)
end

-- 个人 ----------------------------------------------------------------------

-- 获取历史最高段位积分
function ArenaData:getMaxScore()
    return self.maxScore or self:getScore()
end

-- 获取当前段位积分
function ArenaData:getScore()
    return self.score
end

-- 获取当前段位id
function ArenaData:getGradingId()
    return self.curGradingId
end

-- 获取当前段位
function ArenaData:getFormartMaxGrading()
    local id = self:getGradingIdFromScore(self.maxScore) or self.curGradingId
    if not id then
        id = 0
    end
    id = id % 100 -- 取余数
    return id
end

-- 获取自己的排名
function ArenaData:getRank()
    return self.rank
end

-- 获取最近战况
function ArenaData:getRecentBattleByIdx(index)
    if self.recentBattle == nil then
        return nil
    end
    return self.recentBattle[index]
end

-- 获取免费重新匹配cd， 大于0代表在cd中，小于等于0代表cd冷却已好，可以免费Rematch
function ArenaData:getFreeRematchCd()
    if self.matchFreeCdEndTime <= 0 then
        -- cd已经冷却
        return self.matchFreeCdEndTime
    end
    return self.matchFreeCdEndTime - Time:getServerTime()
end

-- 是否能看视频重新匹配
function ArenaData:canAdRematch()
    return self:getFreeRematchCd() > 0 and self.todayAdRematchCount < self:getRematchAdDailyMaxNum()
end

-- 获取上赛季段位id
function ArenaData:getLastGradingId()
    local lastRank = self:getRankCfg(self.season - 1)
    local seasonId,grading = nil
    for id, data in pairs(lastRank) do
        if self.lastScore >= data.score and (grading == nil or grading.score < data.score) then
            seasonId, grading = id, data
        end
    end
    return seasonId
end

-- 是否有可领取的上赛季奖励
function ArenaData:hasSeasonReward()
    if self.lastChallengeCount <= 0 then
        return false
    end
    if self.seasonRewardGot == self.season - 1 then
        return false
    end

    return true
end

-- 是否有入口红点
function ArenaData:hasEntranceRedDot()
    return DataManager.BagData.ItemData:getItemNumById(GConst.ItemConst.ITEM_ID_ARENA_TICKET) > 0 or self:hasSeasonReward() or self:hasGradingRewardRedDot()
end

-- 广告宝箱 ----------------------------------------------------------------------

-- 获取当前段位ad宝箱奖励
function ArenaData:getAdBoxRewards(isWin)
    if isWin then
        return self.cfgRank[self.curGradingId].win_adbox
    else
        return self.cfgRank[self.curGradingId].lose_adbox
    end
end

-- 是否有广告宝箱
function ArenaData:hasAdBox(isWin, checkCount)
    if isWin then
        if checkCount and (checkCount % GFunc.getConstIntValue("arena_win_adbox")) == 0 then
            local todayMax = GFunc.getConstIntValue("arena_win_adbox_limit")
            return self.todayWinAdBoxCount < todayMax
        end
    else
        if checkCount and (checkCount % GFunc.getConstIntValue("arena_lose_adbox")) == 0 then
            local todayMax = GFunc.getConstIntValue("arena_lose_adbox_limit")
            return self.todayLoseAdBoxCount < todayMax
        end
    end

    return false
end

-- 升段奖励 ----------------------------------------------------------------------

-- 获取段位奖励配置
function ArenaData:getGradingRewardCfg()
    if self.gradingRewardCfg == nil then
        self.gradingRewardCfg = ConfigManager:getConfig("arena_gift")
    end

    return self.gradingRewardCfg
end

-- 获取段位道具奖励
function ArenaData:getGradingRewardsItem(id)
    return self:getGradingRewardCfg()[id].reward
end

-- 获取段位英雄奖励， nil为没有
function ArenaData:getGradingRewardsHero(id)
    return self:getGradingRewardCfg()[id].unlock_hero
end

-- 获取当前列表定位所在档位的index
function ArenaData:getCurTargetIndex()
    -- 判断最低奖励可领取
    for id, data in ipairs(self:getGradingRewardCfg()) do
        if self:canGetGradingReward(id) and not self:isReceivedGradingReward(id) then
            return id
        end
    end

    return self:getCurGradingRewardId()
end

-- 获取当前所在段位奖励的id
function ArenaData:getCurGradingRewardId()
    local curScore = self:getScore()
    for id, data in ipairs(self:getGradingRewardCfg()) do
        local prog = self:getGradingRewardProgressByScore(id, curScore)
        if prog >= 0 and prog < 1 then
            return id
        end
    end

    return 1
end

-- 段位奖励是否可领取
function ArenaData:canGetGradingReward(id)
    return self:getMaxScore() >= self:getGradingRewardCfg()[id].score
end

-- 段位奖励是否已领取
function ArenaData:isReceivedGradingReward(id)
    if self.gotGradingRewardIds == nil then
        return false
    end

    return table.containValue(self.gotGradingRewardIds, id)
end

-- 获取积分对应的段位奖励档位进度
function ArenaData:getGradingRewardProgressByScore(id, score)
    local cfg = self:getGradingRewardCfg()
    local point = cfg[id].score

    local rangeMin = 0
    local rangeMax = 0
    if id - 1 > 0 then
        rangeMin = point - math.floor((point - cfg[id - 1].score) / 2)
    else
        rangeMin = self:getSeasonGradingMinScore()
    end
    if id + 1 <= #cfg then
        rangeMax = point + math.floor((cfg[id + 1].score - point) / 2)
    else
        rangeMax = point
    end

    -- Logger.logHighlight(id.."档位范围:"..rangeMin.."->"..rangeMax)

    if id == 1 then
        -- 第一个档位特殊处理
        local prog = 0.5

        if score > point then
            local half = ((score - point) / (rangeMax - point)) * 0.5
            prog = prog + half
        end
        return prog
    end

    if score >= rangeMax then
        -- 超过进度条
        return 1
    elseif score < rangeMin then
        --低于进度条
        return -1
    elseif score == rangeMin then
        -- 进度最低值
        return 0
    else
        -- 在进度条范围内
        return (score - rangeMin) / (rangeMax - rangeMin)
    end
end

-- 是否有段位奖励入口红点
function ArenaData:hasGradingRewardRedDot()
    -- 有奖励可领取
    for id, data in pairs(self:getGradingRewardCfg()) do
        if self:canGetGradingReward(id) and not self:isReceivedGradingReward(id) then
            return true
        end
    end

    return false
end

-- 奖励段位是否是段位分界线
function ArenaData:isGradingMin(id)
    local score = self:getGradingRewardCfg()[id].score

    for id, data in pairs(self.cfgRank) do
        if score == data.score then
            return true
        end
    end

    return false
end

local GRADING_REWARD_COMMON_SIZE = 220
local GRADING_REWARD_GRADING_SIZE = 460
local GRADING_REWARD_UNLOCK_SIZE = 570

-- 获取段位奖励项的高
function ArenaData:getGradingRewardItemHeight(id)
    if self:getGradingRewardCfg()[id].unlock_hero ~= nil then
        -- 有解锁的英雄
        return GRADING_REWARD_UNLOCK_SIZE
    elseif self:isGradingMin(id) then
        -- 是段位分界线
        return GRADING_REWARD_GRADING_SIZE
    else
        -- 普通奖励
        return GRADING_REWARD_COMMON_SIZE
    end
end

-- 获取当前列表定位所在档位的高度
function ArenaData:getCurTargetPosY()
    -- 判断最低奖励可领取
    local totalHeight = 0
    for id, data in ipairs(self:getGradingRewardCfg()) do
        if self:canGetGradingReward(id) and not self:isReceivedGradingReward(id) then
            -- Logger.logHighlight(id)
            return totalHeight
        end

        totalHeight = totalHeight + self:getGradingRewardItemHeight(id)
    end

    -- 判断当前档位
    totalHeight = 0
    local curScore = self:getScore()
    for id, data in ipairs(self:getGradingRewardCfg()) do
        local prog = self:getGradingRewardProgressByScore(id, curScore)
        if prog >= 0 and prog < 1 then
            return totalHeight
        end

        totalHeight = totalHeight + self:getGradingRewardItemHeight(id)
    end

    return 0
end

-- 事件处理 ----------------------------------------------------------------------

function ArenaData:onDayChanged()
    self.todayWinAdBoxCount = 0
    self.todayLoseAdBoxCount = 0
end

-- 获取到排行榜数据
function ArenaData:onRankDataReceived(rank, list)
    self.rank = rank
    self.rankList = list
end

-- 获取到排行榜编队信息
function ArenaData:onRankFormationReceived(id, formation)
    if self.rankFormation == nil then
        self.rankFormation = {}
    end
    self.rankFormation[id] = formation
    self:setDirty()
end

-- 获取到最近战况数据
function ArenaData:onRecentBattleReceived(recentBattle)
    table.sort(recentBattle, function(a, b)
        -- 由新到旧
        return a.occur_at > b.occur_at
    end)
    self.recentBattle = recentBattle
end

-- 获取到匹配数据
function ArenaData:onMatchInfoReceived(match)
    self.matchInfo = match
    self.matchSuccessTime = Time:getServerTime()
    self.matchFreeCdEndTime = Time:getServerTime() + self:getRematchRefreshTime()
    self:setDirty()
end

-- 结算战斗数据
function ArenaData:onBattleResultReceived(score, settlement, result)
    self.matchInfo = nil
    -- 战斗记录改变
    if self.recentBattle == nil then
        self.recentBattle = {}
    end
    table.remove(self.recentBattle, #self.recentBattle)
    table.insert(self.recentBattle, 1, settlement)
    -- 积分改变
    self.score = score
    self.curGradingId = self:getGradingIdFromScore(self.score)

    local beforeMaxGrading = self.maxScore
    self.maxScore = result.highest_score_in_history
    if self:getGradingIdFromScore(beforeMaxGrading) ~= self:getGradingIdFromScore(self.maxScore) then
        DataManager.HeroData:checkIfCanShowHeroUnlockDan(self:getFormartMaxGrading())
    end
    if beforeMaxGrading < self.maxScore then
        ModuleManager.TaskManager:addTaskProgress(GConst.TaskConst.TASK_TYPE.X_ARENA_GRADING, self.maxScore - beforeMaxGrading)
    end

    self:setDirty()
end

function ArenaData:updateTotalFightCount(data)
    self.totalWinCount = data.total_win_count or 0 -- 总胜利次数
    self.totalLoseCount = data.total_lose_count or 0 -- 总失败次数
    self.totalFightCount = self.totalWinCount + self.totalLoseCount
end

function ArenaData:getTotalFightCount()
    return self.totalFightCount or 0
end

-- 已领取上赛季奖励
function ArenaData:onLastSeasonRewardReceived()
    self.seasonRewardGot = self.season - 1
    self:setDirty()
end

-- 跳过匹配cd
function ArenaData:onOverFreeRematchCD(isAd)
    self.matchFreeCdEndTime = -1
    if isAd then
        self.todayAdRematchCount = self.todayAdRematchCount + 1
    end
    self:setDirty()
end

-- 赛季改变
function ArenaData:onSeasonChanged()
    self.matchInfo = nil
    self:setDirty()
end

-- 领取段位奖励
function ArenaData:onGradingRewardReceived(ids)
    self.gotGradingRewardIds = table.addArray(self.gotGradingRewardIds, ids)
    self:setDirty()
end

return ArenaData