-- 战区争霸(普通服)

local Config = require("Config")
local Msg = require("core.Msg")
local Lang = require("common.Lang")
local ObjHuman = require("core.ObjHuman")
local Broadcast = require("broadcast.Broadcast")
local RoleSystemLogic = require("roleSystem.RoleSystemLogic")
local RoleSystemDefine = require("roleSystem.RoleSystemDefine")
local CombatDefine = require("combat.CombatDefine")
local RoleDBLogic = require("role.RoleDBLogic")
local CombatPosLogic = require("combat.CombatPosLogic")
local CombatLogic = require("combat.CombatLogic")
local MailExcel = require("excel.mail")
local Util = require("common.Util")
local InnerMsg = require("core.InnerMsg")
local RoleHeadLogic = require("role.RoleHeadLogic")
local MailManager = require("mail.MailManager")
local AreaBattleConfig = require("excel.areaBattle").reward
local Log = require("common.Log")
local Timer = require("core.Timer")
local CommonDB = require("common.CommonDB")
local RoleSystemExcel = require("excel.roleSystem")
local Grid = require("bag.Grid")
local BillboardLogic = require("billboard.BillboardLogic")
local BillboardDefine = require("billboard.BillboardDefine")
local AreaBattleDefine = require("areaBattle.AreaBattleDefine")


local mailQueue = {
    uuidArray = {},
    insertMaxNum = 100, -- 一次最多插入数据库的邮件数量
    repeatMaxTimes = 3, -- 重试次数
    repeatTb = {}
}

-- 检查是否开启
local function openCheck()
    local wDay = Util.getWeekDay()
    if not table.find(AreaBattleDefine.OPEN_WDAY_AREA, wDay) then
        return false
    end

    local now = os.time()
    local toDayStartTime = Util.getDayStartTime(now)

    if wDay == AreaBattleDefine.OPEN_WDAY_AREA[1] and now < (toDayStartTime + AreaBattleDefine.PREPARE_STATE_START_SEC) then
        return false
    end

    if wDay == AreaBattleDefine.OPEN_WDAY_AREA[2] and now > (toDayStartTime + AreaBattleDefine.END_STATE_START_SEC) then
        return false
    end

    return true
end

-- 生成符合协议数据结构的奖励数据
local function populateAwardMsg(msgTb, awardCfg)
    for idx, itemCfg in ipairs(awardCfg) do
        msgTb[0] = idx
        Grid.makeItem(msgTb[idx], itemCfg[1], itemCfg[2])
    end
end

-- 生成符合协议数据结构的英雄数据
local function populateObjMsg(net, data)
    net.name = data.name or ""
    net.power = data.power or 0
    net.head = data.head or 0
    net.headFrame = data.headFrame or 0
    net.playerLv = data.playerLv or 90

    net.heroList[0] = 0
    for k, heroInfo in ipairs(data.heroList) do
        net.heroList[0] = k
        net.heroList[k].heroId = heroInfo.heroId or 1001
        net.heroList[k].heroColor = heroInfo.heroColor or 0
        net.heroList[k].heroStar = heroInfo.heroStar or 1
        net.heroList[k].heroLv = heroInfo.heroLv or 1
        net.heroList[k].heroCamp = heroInfo.heroCamp or 999
    end
end


local function sendMail(mailId, receiverUuid, itemArray)
    if not mailId or not receiverUuid then
        return
    end

    local mailCfg = MailExcel.mail[mailId]
    local content = mailCfg.content
    MailManager.add(MailManager.SYSTEM, receiverUuid, mailCfg.title, content, itemArray, mailCfg.senderName or "GM")
end

local function writeLog(logStr)
    Log.write(Log.LOGID_OSS_AREABATTLE_AWARD, logStr)
end


function mailQueue:add(playerInfo)
    table.insert(self.uuidArray, playerInfo)
end

function mailQueue:clear()
    Util.cleanTable(self.uuidArray)
    Util.cleanTable(self.repeatTb)
end

function mailQueue:insertDB()
    local maxNum = math.min(self.insertMaxNum, #self.uuidArray)

    for i=1, maxNum do
        local resTag = AreaBattleDefine.MAIL_SUCC_TAG
        local playerInfo = table.remove(self.uuidArray)
        local uuid = playerInfo[1]
        local awardType = playerInfo[3]

        local ok, err = pcall(sendMail, AreaBattleDefine.AWARDTYPE2MAILID[awardType], uuid, playerInfo[2])
        if not ok then
            if not self.repeatTb[uuid] or self.repeatTb[uuid] < self.repeatMaxTimes then
                mailQueue:add(playerInfo)
                self.repeatTb[uuid] = (self.repeatTb[uuid] or 0) + 1
            end

            resTag = AreaBattleDefine.MAIL_FAIL_TAG
        end

        local str = string.format("Result: %s, playerUuid: %s, AwardType: %d, insertErrTimes: %d, errInfo: %s",
                    resTag, uuid, awardType, self.repeatTb[uuid] or 0, err)

        writeLog(str)
    end

    if #self.uuidArray > 0 then
        Timer.addLater(3, mailQueue.insertDB, mailQueue)
    end
end

local function insertPlayerAward(playerArr, battlePlyerArr, awardArr, awardType)
    local len = #playerArr
    for _, uuid in pairs(battlePlyerArr or {}) do
        len = len + 1
        local item = {}
        for idx, itemInfo in ipairs(awardArr) do
            item[idx] = {itemInfo[1], itemInfo[2]}
        end
        playerArr[len] = {uuid, item, awardType}
    end
end

local function genPlayerAwardArray(srvBattleRes)
    local DataCache = require("core.DataCache")
    local playerUidList = DataCache.Get_PlayerUuidList()

    local winAwardType, defeateAwardType, normalAwardType = AreaBattleDefine.AWARD_TYPE1, AreaBattleDefine.AWARD_TYPE2, AreaBattleDefine.AWARD_TYPE3
    local battleWinAwward, battleDefeatAward, normalAward = AreaBattleConfig[1].winAward1, AreaBattleConfig[1].winAward2, AreaBattleConfig[1].winAward3
    if srvBattleRes.isWin == 0 then
        winAwardType, defeateAwardType, normalAwardType = AreaBattleDefine.AWARD_TYPE4, AreaBattleDefine.AWARD_TYPE5, AreaBattleDefine.AWARD_TYPE6
        battleWinAwward, battleDefeatAward, normalAward = AreaBattleConfig[1].defeatAward1, AreaBattleConfig[1].defeatAward2, AreaBattleConfig[1].defeatAward3
    end

    local len = 0
    local playerAwardArray = {}

    if srvBattleRes.isBye == 0 then -- 非轮空服
        insertPlayerAward(playerAwardArray, srvBattleRes.winPlayerArr, battleWinAwward, winAwardType)
        insertPlayerAward(playerAwardArray, srvBattleRes.winPlayerArr, normalAward, normalAwardType)

        insertPlayerAward(playerAwardArray, srvBattleRes.defeatPlayerArr, battleDefeatAward, defeateAwardType)
        insertPlayerAward(playerAwardArray, srvBattleRes.defeatPlayerArr, normalAward, normalAwardType)
    else
        -- 轮空服
        normalAwardType = AreaBattleDefine.AWARD_TYPE7
        normalAward = AreaBattleConfig[1].defeatAward3
    end


    len = #playerAwardArray
    for uuid in pairs(playerUidList) do
        if not table.find(srvBattleRes.winPlayerArr, uuid) and not table.find(srvBattleRes.defeatPlayerArr, uuid) then
            len = len + 1
            local item = {}
            for idx, itemInfo in ipairs(normalAward) do
                item[idx] = {itemInfo[1], itemInfo[2]}
            end

            playerAwardArray[len] = {uuid, item, normalAwardType}
        end
    end


    if len == 0 then
        return
    end

    return playerAwardArray
end


-- 开服天数检查
local function checkSvrOpenDays()
    local openDay = CommonDB.getServerOpenDay()
    local ctrlCfg = RoleSystemExcel.roleSystem[RoleSystemDefine.ROLE_SYS_ID_1306]
    if ctrlCfg.openSvrDay > openDay then
        return false
    end

    return true
end

-- 单个英雄数据
-- local function getHeroInfo(human, targetHeroUuid)
--     local HeroLogic = require("hero.HeroLogic")
--     local heroGrid = HeroLogic.getHeroGridByUuid(human, targetHeroUuid)
--     if heroGrid then
--         local heroCfg = HeroExcel.hero[heroGrid.id]
--         local heroInfo = {
--             heroUuid = targetHeroUuid,
--             heroStar = heroGrid.star,
--             heroLevel = heroGrid.lv,
--             heroCamp =  heroGrid.camp or heroCfg.camp,
--             heroBody = heroCfg.body
--         }

--         return heroInfo
--     end
-- end

-- 获取上阵英雄数据
-- local function getHeroListInfo(human, targetHeroUuid)
--     local len = 0
--     local heroArr = {}

--     local heroList = CombatPosLogic.getCombatHeros(human, CombatDefine.COMBAT_TYPE32)
--     if not next(heroList) then
--         heroList = CombatPosLogic.getCombatHeros(human, CombatDefine.COMBAT_TYPE1)
--     end

--     for _, heroUuid in pairs(heroList) do
--         if not targetHeroUuid or targetHeroUuid == heroUuid then
--             len = len + 1
--             heroArr[len] = getHeroInfo(human, heroUuid)
--         end
--     end

--     if len == 0 then
--         return
--     end
--     return heroArr
-- end

-- 生成玩家展示数据
local function genPlayerShowData(playerUuid)
    local human = ObjHuman.onlineUuid[playerUuid]
    if not human then
        local db = RoleDBLogic.getDb(playerUuid)
        if not db then
            return
        end

        human = {}
        human.db = db
    end

    local showInfo = {}
    showInfo.uuid = playerUuid
    showInfo.power = human.db.zhandouli
    showInfo.name = human.db.name

    showInfo.head = RoleHeadLogic.getRoleAppearance(human, RoleHeadLogic.HEAD_TYPE_1)
    showInfo.headFrame = RoleHeadLogic.getRoleAppearance(human, RoleHeadLogic.HEAD_TYPE_2)
    -- showInfo.heroList = getHeroListInfo(human)
    return showInfo
end

-- 生成本服参战列表
local function genJoinBattleArray()
    local rankList = BillboardLogic.GetRankList(BillboardDefine.TYPE_ZHANDOULI)
    if not rankList then
        return
    end

    local len = 0
    local joinBattleArray = {}
    for _, PlayerData in ipairs(rankList) do
        if len >= AreaBattleDefine.JOIN_BATTLE_MAX_CNT then
            break
        end

        local pkayerInfo = genPlayerShowData(PlayerData.uuid)
        if pkayerInfo then
            len = len + 1
            joinBattleArray[len] = pkayerInfo
        end
    end

    table.shuffle(joinBattleArray)

    return joinBattleArray
end


function getActState(human)
    local state, leftTime = 1, 0

    if not openCheck() then
        state = 0
    end

    return state, leftTime
end

function isOpen(human)
    return RoleSystemLogic.isOpen(human, RoleSystemDefine.ROLE_SYS_ID_1306)
end

function getCombatObjList(human, side, args, combatType)
    if side == CombatDefine.ATTACK_SIDE and not human then return end

    if side == CombatDefine.DEFEND_SIDE and human then
        return
    end

    if not human then
        local uuid = args[1]
        local db = RoleDBLogic.getDb(uuid)
        if not db then
            return
        end
        human = {}
        human.db = db
    end

    local heroList = CombatPosLogic.getCombatHeros(human, combatType)
    if next(heroList) then
        return CombatLogic.getHumanObjList(human, combatType)
    else
        return CombatLogic.getHumanObjList(human, CombatDefine.COMBAT_TYPE1)
    end
end




--------------------------------------------N2C----------------------------------------------------

-- 加入战斗
local function joinBattle()
    local joinBattleArray = genJoinBattleArray()
    -- if not joinBattleArray or #joinBattleArray <= 0 then
    --     return
    -- end
    local msgData = InnerMsg.lw.LW_AREABATTLE_JOIN_BATTLE
    msgData.sourceServerId = Config.SVR_INDEX
    msgData.joinBattleArray = joinBattleArray or {}
    InnerMsg.sendMsg(0, msgData)
end

-- 获取活动基础信息
function GetBaseInfo(human)
    local msgData = InnerMsg.lw.LW_AREABATTLE_BASEINFO_QUERY
    msgData.sourceServerId = Config.SVR_INDEX
    msgData.playerUuid = human.db._id
    InnerMsg.sendMsg(0, msgData)
end

-- 获取本服参战玩家列表
function GetJoinPlayerArr(human)
    -- if not openCheck() then
    --     local msgRet = Msg.gc.GC_AREABATTLE_JOINPLAYER_QUERY
    --     msgRet.errCode = -2
    --     msgRet.isJoin = 0
    --     msgRet.playerArray[0] = 0
    --     return Msg.send(msgRet, human.fd)
    -- end

    local msgData = InnerMsg.lw.LW_AREABATTLE_JOINPLAYER_QUERY
    msgData.sourceServerId = Config.SVR_INDEX
    msgData.playerUuid = human.db._id
    InnerMsg.sendMsg(0, msgData)
end

-- 获取区服匹配列表(两两匹配)
function GetMatchList(human)
    -- if not openCheck() then
    --     local msgRet = Msg.gc.GC_AREABATTLE_MATCHLIST_QUERY
    --     msgRet.errCode = -2
    --     msgRet.matchList[0] = 0
    --     return Msg.send(msgRet, human.fd)
    -- end
    local msgData = InnerMsg.lw.LW_AREABATTLE_MATCHLIST_QUERY
    msgData.sourceServerId = Config.SVR_INDEX
    msgData.playerUuid = human.db._id
    InnerMsg.sendMsg(0, msgData)
end

--获取录像
function GetCombatVideo(human)
    -- if not openCheck() then
    --     local msgRet = Msg.gc.GC_GODSAREA_QUERY_COMBATVIDEO
    --     msgRet.errCode = -2
    --     msgRet.leftWinTimes = 0
    --     msgRet.rightWinTimes = 0
    --     msgRet.leftSrvName = ""
    --     msgRet.leftSrvPower = 0
    --     msgRet.rightSrvName = ""
    --     msgRet.rightSrvPower = 0
    --     msgRet.videoList[0] = 0
    --     return Msg.send(msgRet, human.fd)
    -- end
    local msgData = InnerMsg.lw.LW_AREABATTLE_VIDEOSHOW_QUERY
    msgData.sourceServerId = Config.SVR_INDEX
    msgData.playerUuid = human.db._id
    InnerMsg.sendMsg(0, msgData)
end

-- 获取奖励列表
function GetAwardInfo(human)
    local msgRet = Msg.gc.GC_AREABATTLE_REWARD_QUERY

    populateAwardMsg(msgRet.winJoinAward, AreaBattleConfig[1].winAward1)
    populateAwardMsg(msgRet.winJoinAward2, AreaBattleConfig[1].winAward2)
    populateAwardMsg(msgRet.winNormalAward, AreaBattleConfig[1].winAward3)

    populateAwardMsg(msgRet.defaultJoinAward, AreaBattleConfig[1].defeatAward1)
    populateAwardMsg(msgRet.defaultJoinAward2, AreaBattleConfig[1].defeatAward2)
    populateAwardMsg(msgRet.defaultNormalAward, AreaBattleConfig[1].defeatAward3)

    Msg.send(msgRet, human.fd)
end




--------------------------------------------C2N----------------------------------------------------

-- 收到活动开启通知
function C2N_BattleStart()
    if not checkSvrOpenDays() then
        return
    end
    joinBattle()
end

-- 收到活动基础信息
function C2N_BaseInfo(msg)
    local human = ObjHuman.onlineUuid[msg.playerUuid]
    if not human then
        return
    end

    local msgRet = Msg.gc.GC_AREABATTLE_BASEINFO_QUERY
    msgRet.stage = msg.stage
    msgRet.startTime = msg.startTime
    msgRet.endTime = msg.endTime
    Msg.send(msgRet, human.fd)
end

-- 收到本服参战玩家列表
function C2N_JoinPlayerArr(msg)
    local human = ObjHuman.onlineUuid[msg.playerUuid]
    if not human then
        return
    end

    local msgRet = Msg.gc.GC_AREABATTLE_JOINPLAYER_QUERY
    msgRet.errCode = msg.errCode
    msgRet.isJoin = 0
    msgRet.playerArray[0] = 0

    if msg.errCode == 0 then
        local playerArray = msg.playerArray
        table.sort(playerArray, function (a, b)
            return a.power > b.power
        end)

        for k, v in ipairs(playerArray) do
            msgRet.playerArray[0] = k
            msgRet.playerArray[k].name = v.name
            msgRet.playerArray[k].power = v.power
            msgRet.playerArray[k].head = v.head
            msgRet.playerArray[k].headFrame = v.headFrame
            msgRet.playerArray[k].isMe = 0

            if v.uuid == msg.playerUuid then
                msgRet.isJoin = 1
                msgRet.playerArray[k].isMe = 1
            end
        end
    end

    Msg.send(msgRet, human.fd)
end

-- 收到本轮活动的区服匹配列表
function C2N_MatchList(msg)
    local human = ObjHuman.onlineUuid[msg.playerUuid]
    if not human then
        return
    end

    local msgRet = Msg.gc.GC_AREABATTLE_MATCHLIST_QUERY
    msgRet.errCode = 0
    local matchListMsg = msgRet.matchList
    matchListMsg[0] = 0

    if not next(msg.matchList) then
        msgRet.errCode = -1
    else
        for k, v in ipairs(msg.matchList) do
            matchListMsg[0] = k

            --for _, srvInfo in ipairs(v) do
                local leftSrv = v[1]
                local rightSrv = v[2]
                matchListMsg[k].leftSrvName = (leftSrv[1] - AreaBattleDefine.SRV_START_IDX) .. AreaBattleDefine.SRV_NAME_STR
                matchListMsg[k].leftSrvPower = leftSrv[2]

                matchListMsg[k].rightSrvName = (rightSrv[1] - AreaBattleDefine.SRV_START_IDX) .. AreaBattleDefine.SRV_NAME_STR
                matchListMsg[k].rightSrvPower = rightSrv[2]

                matchListMsg[k].mySrvPos = 0
                if leftSrv[1] == Config.SVR_INDEX then
                    matchListMsg[k].mySrvPos = 1
                elseif rightSrv[1] == Config.SVR_INDEX then
                    matchListMsg[k].mySrvPos = 2
                end
            --end
        end
    end

    Msg.send(msgRet, human.fd)
end

-- 收到本服的战斗录像展示数据
function C2N_VideoShowData(msg)
    local human = ObjHuman.onlineUuid[msg.playerUuid]
    if not human then
        return
    end

    local errCode = msg.errCode
    local msgRet = Msg.gc.GC_AREABATTLE_COMBATVIDEO_QUERY
    msgRet.errCode = errCode
    msgRet.leftWinTimes = 0
    msgRet.rightWinTimes = 0
    msgRet.leftSrvName = ""
    msgRet.leftSrvPower = 0
    msgRet.rightSrvName = ""
    msgRet.rightSrvPower = 0
    msgRet.videoList[0] = 0
    if errCode ~= 0 then
        return Msg.send(msgRet, human.fd)
    end

    local srvInfo = msg.srvInfo
    msgRet.leftWinTimes = srvInfo.leftWinTimes
    msgRet.rightWinTimes = srvInfo.rightWinTimes
    msgRet.leftSrvName = (srvInfo.leftSrvId - AreaBattleDefine.SRV_START_IDX) .. AreaBattleDefine.SRV_NAME_STR
    msgRet.leftSrvPower = srvInfo.leftSrvPower
    msgRet.rightSrvName = (srvInfo.rightSrvId - AreaBattleDefine.SRV_START_IDX) .. AreaBattleDefine.SRV_NAME_STR
    msgRet.rightSrvPower = srvInfo.rightSrvPower

    for k, v in ipairs(msg.videoShowData) do
        msgRet.videoList[0] = k
        -- msgRet.videoList[k].atkData = v.atkData
        -- msgRet.videoList[k].defenerData = v.defenerData
        populateObjMsg(msgRet.videoList[k].atkData, v.atkData)
        populateObjMsg(msgRet.videoList[k].defenerData, v.defenerData)
        msgRet.videoList[k].atkFormation = v.atkFormation
        msgRet.videoList[k].defenerFormation = v.defenerFormation
        msgRet.videoList[k].round = v.round
        msgRet.videoList[k].maxRound = v.maxRound
        msgRet.videoList[k].time = v.time
        msgRet.videoList[k].videoUuid = v.videoUuid
        msgRet.videoList[k].result = v.result

        msgRet.videoList[k].myPos = 0
        if k == 1 then
            if v.atkData.uuid == msg.playerUuid then
                msgRet.videoList[k].myPos = 1
            elseif v.defenerData.uuid == msg.playerUuid then
                msgRet.videoList[k].myPos = 2
            end
        end
    end

    Msg.send(msgRet, human.fd)
end

-- 收到给玩家发奖励通知
function C2N_SendReward(msg)
    local playerAwardArray = genPlayerAwardArray(msg.srvBattleRes)
    if not playerAwardArray then
        return
    end

    mailQueue:clear()

    for _, playerInfo in ipairs(playerAwardArray) do
        mailQueue:add(playerInfo)
    end

    mailQueue:insertDB()
end