#!/usr/bin/python
# -*- coding: utf-8 -*-
# @todo: 区服总览(当前服生态)
import CommFunc
import DBOperate
import ConfigParser
import CTGOKSort
import operator
import datetime
import time
import os
import copy
import mylog
import logging
cfg = ConfigParser.ConfigParser()
cfg.read("../../InterfaceConfig.php")
ServerPath = cfg.get("ServerInfo", "ServerPath")
class CrossPlayer():
    
    def __init__(self):
        self.playerLV = 0
        self.fightPower = 0
        return
    
def CrossServerViewInfo(argvDict):
    '''
        按跨服功能分区查询:
    1、活跃玩家人数(选取时间内上线过)总合
    2、活跃玩家人数的等级分布(1~299,之后按每50级一个跨段)
    '''
    
    crossMapDict = {(32010, 32011, 32012, 32013, 32014, 32015, 32016, 32017, 32018, 32019):_(u"跨服天梯"),
                    (32020, 32021, 32022, 32023, 32024, 32025, 32026, 32027, 32028, 32029):_(u"蓬莱仙境"),
                    (32080, 32081, 32082, 32083, 32084, 32085, 32086, 32087, 32088, 32089):_(u"魔化之地"),
                    (32070, 32071, 32072, 32073, 32074, 32075, 32076, 32077, 32078, 32079, 33000, 33001, 33002, 33003, 33004, 33005, 33006, 33007, 33008, 33009):_(u"跨服排位赛"),
                    (32060, 32061, 32062, 32063, 32064, 32065, 32066, 32067, 32068, 32069):_(u"古神战场"),
                    }
    loginZonePlayerDict = {k:{} for k in crossMapDict.keys()}
    
    startDate = argvDict.get("startDate", "")
    endDate = argvDict.get("endDate", "")
    CommFunc.loopMainServerDR(cfg, startDate, endDate, argvDict, checkDrFileNeedParseFunc_CrossServerViewInfo,
                              parseLineFunc_CrossServerViewInfo, loginZonePlayerDict)
    
    spaceStr = "  ";
    printStr = ""
    
    printStr += "%s:%s%s" % (_(u"开始日期"), spaceStr, startDate);
    printStr += "%s~%s" % (spaceStr, spaceStr)
    if endDate:
        printStr += "%s" % (endDate)
    else:
        printStr += _(u"今天")
    printStr += "
"
    for mapKey, zonePlayerDict in loginZonePlayerDict.items():
        mapName = crossMapDict.get(mapKey, str(mapKey))
        printStr += __PrintCrossFuncViewInfo(zonePlayerDict, mapName, spaceStr)
        
    printStr += ""
    print printStr
    return
def __PrintCrossFuncViewInfo(pkZonePlayerDict, funcName, spaceStr):
    printStr = ""
    printStr += "
【%s】
" % funcName
    
    dataList = []
    tdList = ["zoneID", "accIDCount"]
    
    topFPCount = 10 # 前10战力
    lvRangeList = [300] + range(350, 1000, 50)
    lvTitleList = []
    accIDTotal = 0
    lvTotalCountDict = {}
    topFPPlayerList = []
    for zoneID, playerDict in pkZonePlayerDict.items():
        playerList = playerDict.values()
        playerList.sort(key=operator.attrgetter("fightPower"), reverse=True)
        
        accIDCount = len(playerDict) # 总人数
        
        lvCountDict = {} # 等级分布人数
        fightPowerList = []
        for player in playerList:
            playerLV = player.playerLV
            for lv in lvRangeList:
                if playerLV < lv:
                    lvCountDict[lv] = lvCountDict.get(lv, 0) + 1
                    break
            
            if len(fightPowerList) < topFPCount:
                fightPowerList.append(player)
                
        topFPPlayerList.extend(fightPowerList)
        for lv in lvRangeList:
            if lv in lvCountDict:
                continue
            if lv > max(lvCountDict):
                break
            lvCountDict[lv] = 0
            
        for lv, count in lvCountDict.items():
            if lv not in lvTitleList:
                lvTitleList.append(lv)
            lvTotalCountDict[lv] = lvTotalCountDict.get(lv, 0) + count
            
        accIDTotal += accIDCount
        tableInfo = {"zoneID":zoneID, "accIDCount":accIDCount, "topFightPower":getSortDataStr(fightPowerList, "fightPower", spaceStr)}
        tableInfo.update(lvCountDict)
        dataList.append(tableInfo)
        
    lvTitleList.sort()
    tdList.extend(lvTitleList)
    tdList.extend(["topFightPower"])
    
    # 最后一行汇总信息
    topFPPlayerList.sort(key=operator.attrgetter("fightPower"), reverse=True)
    totalInfo = {"zoneID":_(u"总"), "accIDCount":accIDTotal, "topFightPower":getSortDataStr(topFPPlayerList[:topFPCount], "fightPower", spaceStr)}
    totalInfo.update(lvTotalCountDict)
    dataList.append(totalInfo)
    
    styleInfo = {"zoneID":{"title":_(u"分区ID")},
                 "accIDCount":{"title":_(u"总活跃")},
                 "topFightPower":{"align":"left", "title":_(u"战力榜")},
                 }
    for lv in lvTitleList:
        styleInfo[lv] = {"title":"LV < %s" % lv}
    printStr += CommFunc.editTableHtml(dataList, tdList, "", styleInfo, printCount=False)
    
#    printStr += "
"
    return printStr
def checkDrFileNeedParseFunc_CrossServerViewInfo(drFileName, *parseArgs, **kv):
    ''' 检查流向是否需要处理
    @param drFileName: 流向文件名  xxx_日期.txt
    @param *parseArgs: 自定义参数
    @return: isNeed, checkNeedParseRetInfo
    '''
    isNeed = drFileName.startswith("LogInOut_")
    return isNeed, None
def parseLineFunc_CrossServerViewInfo(drName, dateStr, checkNeedParseRetInfo, line, *parseArgs, **kv):
    ''' 解析流向行内容
    @param drName: 流向名
    @param dateStr: 对应日期字符串
    @param checkNeedParseRetInfo: checkDrFileNeedParseFunc 返回的内容
    @param line: 本行内容
    @param *parseArgs: 自定义参数
    '''
    
    loginZonePlayerDict = parseArgs[0]
    
    drDict = eval(line)
    
    # 统计累计登录
    if drName == "LogInOut":
        if drDict["Type"] != "login":
            return
        if "RegCrossZoneID" not in drDict:
            return
        accID = drDict["AccID"]
        mapID = drDict["mapID"]
        
        zonePlayerDict = None
        for mapKey in loginZonePlayerDict.keys():
            if mapID in mapKey:
                zonePlayerDict = loginZonePlayerDict[mapKey]
                break
        if zonePlayerDict == None:
            mapKey = (mapID,)
            loginZonePlayerDict[mapKey] = {}
            zonePlayerDict = loginZonePlayerDict[mapKey]
            
        #playerName = drDict["Name"]
        playerLV = drDict["PlayerLV"]
        fightPower = drDict["fightPower"]
        zoneID = drDict["RegCrossZoneID"]
        
        if zoneID not in zonePlayerDict:
            zonePlayerDict[zoneID] = {}
        if accID not in zonePlayerDict[zoneID]:
            player = CrossPlayer()
            zonePlayerDict[zoneID][accID] = player
        player = zonePlayerDict[zoneID][accID]
        player.playerLV = playerLV
        player.fightPower = fightPower
        
    return
def getServerViewInfo(argvDict):
    ## 非跨服服务器总览
    
    onlineMaxInfo = {}
    loginAccIDList = []
    orderTypeCTGOKInfo = {}
    createRoleInfo = [0, 0] # [创角人数, 首登人数]
    newCTGAccIDInfo = {}
    
    startDate = argvDict.get("startDate", "")
    endDate = argvDict.get("endDate", "")
    CommFunc.loopMainServerDR(cfg, startDate, endDate, argvDict, checkDrFileNeedParseFunc_ServerViewInfo,
                              parseLineFunc_ServerViewInfo, onlineMaxInfo, loginAccIDList, orderTypeCTGOKInfo, createRoleInfo, newCTGAccIDInfo)
    
    # 以下榜单数据有一定延迟,非实时排行数据,存档时间5分钟
    # 合并数据 - 战力榜
    topOrderCount = CommFunc.toInt(argvDict.get("topOrderCount"), 3)
    dboper = DBOperate.DBOper(ServerPath)
    col = dboper.db["tagDBPlayer"]
    spec = {}
    fields = {'_id':0, "FightPower":1, "FightPowerEx":1}
    fightPowerRet = col.find(spec, fields).sort([("FightPowerEx", -1), ("FightPower", -1)]).limit(topOrderCount)
    
    # 合并数据 - 等级榜
    fields = {'_id':0, "LV":1}
    LVRet = col.find(spec, fields).sort([("LV", -1)]).limit(topOrderCount)
    
    # 实际排行 - 战力
    col = dboper.db["tagDBBillboard"]
    spec = {"Type":0}
    fields = {'_id':0, "CmpValue":1, "CmpValue2":1}
    billboard_FightPowerRet = col.find(spec, fields).sort([("CmpValue", -1), ("CmpValue2", -1)]).limit(topOrderCount)
    isFightPowerExVer = False # 战力是否超过20E的版本
    for fpData in copy.deepcopy(billboard_FightPowerRet):
        if fpData["CmpValue2"] > 0: # 理论上只要有数据中比较值2大于0就是超过20E的版本,比较值2为求余亿的部分
            isFightPowerExVer = True
            break
        
    # 实际排行 - 等级
    spec = {"Type":4}
    fields = {'_id':0, "CmpValue":1}
    billboard_LVRet = col.find(spec, fields).sort([("CmpValue", -1)]).limit(topOrderCount)
    
    # 服务器其他信息
    openServerTimeStr, worldLV, serverDay, isMixServer, mixServerDay = "", 0, 0, 0, 0
    openServerTiemServerDay = 0
    zoneID = 0
    col = dboper.db["tagDBGameServerEventTrig"]
    spec = {"EventID":{"$in":["InitOpenServerTime", "WorldAverageLv", "ServerDay", "IsMixServer", "MixServerDay", "CrossPKZoneID"]}}
    #fields = {}
    eventRet = col.find(spec)
    for eventInfo in eventRet:
        eventName = eventInfo["EventID"]
        eventValue = eventInfo["IsEvent"]
        if eventName == "InitOpenServerTime":
            openServerTime = eventValue
            openServerTimeStr = "" if not openServerTime else time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(openServerTime))
            if openServerTimeStr:
                openServerDatetime = datetime.datetime.strptime(openServerTimeStr.split(" ")[0], "%Y-%m-%d")
                openServerTiemServerDay = (datetime.datetime.today() - openServerDatetime).days + 1
                
        elif eventName == "WorldAverageLv":
            worldLV = eventValue
        elif eventName == "ServerDay":
            serverDay = eventValue + 1
        elif eventName == "IsMixServer":
            isMixServer = eventValue
            if not isMixServer:
                mixServerDay = 0
        elif eventName == "MixServerDay":
            if isMixServer:
                mixServerDay = eventValue + 1
        elif eventName == "CrossPKZoneID":
            zoneID = eventValue
            
    # 关闭
    dboper.close()
    
    spaceStr = "  ";
    printStr = ""
    
    printStr += "%s:%s%s" % (_(u"开始日期"), spaceStr, startDate);
    printStr += "%s~%s" % (spaceStr, spaceStr)
    if endDate:
        printStr += "%s" % (endDate)
    else:
        printStr += _(u"今天")
    printStr += "
"
    
    printStr += "%s:%s%s%s人;%s %s:%s%s%s人;%s 
" \
        % (_(u"创角人数"), spaceStr, createRoleInfo[0], spaceStr, spaceStr * 2,
           _(u"首登人数"), spaceStr, createRoleInfo[1], spaceStr, spaceStr * 2
           )
    
    printStr += "%s:%s%s%s人;%s %s:%s%s%s人;%s %s:%s%s
" \
        % (_(u"最高在线"), spaceStr, onlineMaxInfo.get("OnLinePlayerCount", 0) - onlineMaxInfo.get("tjgOnlineCnt", 0), spaceStr, spaceStr * 2,
           _(u"额外脱机"), spaceStr, onlineMaxInfo.get("tjgOnlineCnt", 0), spaceStr, spaceStr * 2,
           _(u"时间"), spaceStr, onlineMaxInfo.get("time", ""))
        
    allCtgAccIDList = []
    printStr += "%s:%s%s%s人
" % (_(u"累计登录"), spaceStr, len(loginAccIDList), spaceStr)
    for payOrderType, ctgOKInfo in orderTypeCTGOKInfo.items():
        orderTypeName = CommFunc.getPayOrderTypeName(payOrderType)
        ctgOKTotal, ctgAccIDList = ctgOKInfo
        if payOrderType not in CTGOKSort.PayByMoneyOrderTypeList:
            ctgOKTotal = CommFunc.coinToY(ctgOKTotal, CommFunc.getPayOrderCoinRate(payOrderType))
        ctgPlayerCount = len(ctgAccIDList)
        ctgAvgValue = "%.2f" % (ctgOKTotal / ctgPlayerCount) if ctgPlayerCount else 0
        printStr += "%s:%s%s%s%s;%s %s:%s%s%s;%s %s:%s%s%s%s
" \
            % (_(u"累计充值"), spaceStr, ctgOKTotal, spaceStr, orderTypeName, spaceStr * 2,
               _(u"人数"), spaceStr, ctgPlayerCount, spaceStr, spaceStr * 2,
               _(u"人均"), spaceStr, ctgAvgValue, spaceStr, orderTypeName)
        for accID in ctgAccIDList:
            if accID not in allCtgAccIDList:
                allCtgAccIDList.append(accID)
    if not orderTypeCTGOKInfo:
        printStr += "%s:%s 0 
" % (_(u"累计充值"), spaceStr)
    elif len(orderTypeCTGOKInfo) > 1:
        printStr += "%s:%s%s
" % (_(u"累计充值人数"), spaceStr, len(allCtgAccIDList))     
    printStr += "%s:" % (_(u"新增充值人数"))     
    newCTGDateList = newCTGAccIDInfo.keys()
    newCTGDateList.sort()
    newCTGDateList = newCTGDateList[-7:]
    for dateStr in newCTGDateList:
        newCTGCount = len(newCTGAccIDInfo[dateStr])
        printStr += "%s%s:%s" % (spaceStr, dateStr, newCTGCount) 
    if not newCTGDateList:
        printStr += "%s0" % spaceStr   
    printStr += "
"   
    # 战力
    printStr += "%s
" % _(u"以下排行榜非实时数据")
    printStr += "%s:%s%s
" % (_(u"所有角色战力排行"), spaceStr, getSortDataStr(fightPowerRet, "FightPower", spaceStr, "FightPowerEx"))
    if isFightPowerExVer:
        printStr += "%s:%s%s
" % (_(u"活跃角色战力排行"), spaceStr, getSortDataStr(billboard_FightPowerRet, "CmpValue2", spaceStr, "CmpValue"))        
    else:
        printStr += "%s:%s%s
" % (_(u"活跃角色战力排行"), spaceStr, getSortDataStr(billboard_FightPowerRet, "CmpValue", spaceStr))
    printStr += "%s:%s%s
" % (_(u"所有角色等级排行"), spaceStr, getSortDataStr(LVRet, "LV", spaceStr))
    printStr += "%s:%s%s
" % (_(u"活跃角色等级排行"), spaceStr, getSortDataStr(billboard_LVRet, "CmpValue", spaceStr))
    printStr += "%s:%s%s%s %s:%s%s %s:%s%s %s:%s%s %s:%s%s %s(%s)%s" % \
                    (_(u"开服时间"), spaceStr, openServerTimeStr, spaceStr * 2,
                     _(u"开服天"), serverDay, spaceStr,
                     _(u"是否合服"), isMixServer, spaceStr,
                     _(u"合服天"), mixServerDay, spaceStr,
                     _(u"世界等级"), worldLV, spaceStr,
                     spaceStr * 2, _(u"凌晨0~5点查询可能误差1天"), spaceStr * 2
                     )
                    
    # 跨服PK分区显示样式,不同分区禁止规划合服合在一起
    if zoneID:
        crossPKZoneStyleList = [["#ff0000", "#ffffff"], ["#00ff00", "#ffffff"], ["#ffff00", "#515a6e"], ["#2d8cf0", "#ffffff"], ["#00ffff", "#ffffff"]]
        bgColor, zoneIDColor = ["", ""] if zoneID > len(crossPKZoneStyleList) else crossPKZoneStyleList[zoneID - 1]
        printStr += "%s%s%s%s" \
            % (bgColor, _(u"跨服PK分区"), spaceStr, zoneIDColor, zoneID, spaceStr)
            
    printStr += "
"
    printStr += ""
    print printStr
    return
def getSortDataStr(dataList, showDataKey, spaceStr, showDataKeyEx=None):
    showStr = ""
    for i, dataInfo in enumerate(dataList):
        if i != 0:    
            showStr += ",%s" % (spaceStr)
        if isinstance(dataInfo, dict):
            value = dataInfo[showDataKey]
            if showDataKeyEx != None and showDataKeyEx in dataInfo:
                value += dataInfo[showDataKeyEx] * 100000000 
        else:
            value = getattr(dataInfo, showDataKey)
            if showDataKeyEx != None and hasattr(dataInfo, showDataKeyEx):
                value += getattr(dataInfo, showDataKeyEx) * 100000000
        showStr += CommFunc.transformNum(value)
    return showStr
def checkDrFileNeedParseFunc_ServerViewInfo(drFileName, *parseArgs, **kv):
    ''' 检查流向是否需要处理
    @param drFileName: 流向文件名  xxx_日期.txt
    @param *parseArgs: 自定义参数
    @return: isNeed, checkNeedParseRetInfo
    '''
    isNeed = (drFileName.startswith("OnLinePlayerCount_") or drFileName.startswith("LogInOut_") or drFileName.startswith("CTGOK_") \
              or drFileName.startswith("FirstLogin_") or drFileName.startswith("tagDBCreateRole_"))
    return isNeed, None
def parseLineFunc_ServerViewInfo(drName, dateStr, checkNeedParseRetInfo, line, *parseArgs, **kv):
    ''' 解析流向行内容
    @param drName: 流向名
    @param dateStr: 对应日期字符串
    @param checkNeedParseRetInfo: checkDrFileNeedParseFunc 返回的内容
    @param line: 本行内容
    @param *parseArgs: 自定义参数
    '''
    
    onlineMaxInfo, loginAccIDList, orderTypeCTGOKInfo, createRoleInfo, newCTGAccIDInfo = parseArgs
    
    # 统计最高在线
    if drName == "OnLinePlayerCount":
        drDict = eval(line)
        #{'OnLinePlayerCount': 2, 'tjgOnlineCnt': 28, 'platformOLDict': 'qkbtgame', 'time': '2021-01-01 00:00:46'} 按平台记录
        # ...
        #{'OnLinePlayerCount': 30, 'tjgOnlineCnt': 28, 'platformOLDict': {}, 'time': '2021-01-01 00:00:46'} 最后一条汇总
        if drDict["platformOLDict"] != {}:
            # 暂只处理汇总的信息
            return
        nowMax = onlineMaxInfo.get("OnLinePlayerCount", 0) - onlineMaxInfo.get("tjgOnlineCnt", 0)
        drOnlineCount = drDict["OnLinePlayerCount"]
        drTjCount = drDict["tjgOnlineCnt"]
        realOnlineCount = drOnlineCount - drTjCount # 实际在线
        if realOnlineCount < nowMax:
            return
        if realOnlineCount == nowMax and drDict["tjgOnlineCnt"] < onlineMaxInfo.get("tjgOnlineCnt", 0):
            return
        onlineMaxInfo.update(drDict)
        
    # 统计累计登录
    elif drName == "LogInOut":
        if "logout" in line:
            return
        drDict = eval(line)
        if drDict["Type"] != "login":
            return
        accID = drDict["AccID"]
        if accID in loginAccIDList:
            return
        loginAccIDList.append(accID)
        
    # 统计充值
    elif drName == "CTGOK":
        drDict = eval(line)
        payOrderType = drDict.get("payOrderType", CTGOKSort.DefaultOrderType)
        ctgOKTotal, ctgAccIDList = orderTypeCTGOKInfo.get(payOrderType, [0, []])
        if payOrderType in CTGOKSort.PayByMoneyOrderTypeList:
            orderValue = drDict["orderMoneyValue"]
        else:
            if not CTGOKSort.IsRealCTG(drDict):
                return
            orderValue = drDict["orderCoin"]
        ctgOKTotal += orderValue
        accID = drDict["AccID"]
        if accID not in ctgAccIDList:
            ctgAccIDList.append(accID)
        orderTypeCTGOKInfo[payOrderType] = [ctgOKTotal, ctgAccIDList]
        
        ctgRealFirstTimeStr = drDict.get("ctgRealFirstTime", "")
        if ctgRealFirstTimeStr.startswith(dateStr):
            newCTGAccIDList = newCTGAccIDInfo.get(dateStr, [])
            if accID not in newCTGAccIDList:
                newCTGAccIDList.append(accID)
            newCTGAccIDInfo[dateStr] = newCTGAccIDList
    # 创角
    elif drName == "tagDBCreateRole":
        createRoleInfo[0] += 1
        
    # 首登
    elif drName == "FirstLogin":
        createRoleInfo[1] += 1
        
    return
def main():
    CommFunc.setdefaultencoding()
    argvDict = CommFunc.parse_args()
    mylog.InitMyLog(argvDict.get("eventType", ""))
    CommFunc.gettextInstall(argvDict.get("lang", ""))
    dbIni = ConfigParser.ConfigParser()
    dbIni.read(os.path.join(ServerPath, "db\PyMongoDataServer\PyMongoDataServer.ini"))
    isCrossServer = dbIni.has_option("Merge", "IsMergeServer") and dbIni.getint("Merge", "IsMergeServer") == 1
    if isCrossServer:
        CrossServerViewInfo(argvDict)
    else:
        getServerViewInfo(argvDict)
    return
if __name__ == "__main__":
    try:
        main()
    except:
        CommFunc.printExceptionError()