# -*- coding: utf-8 -*-
import os
import os.path
import sys
import json
import levelData_pb2
import csv

# 获取当前工作目录
current_dir = os.getcwd()
# 获取脚本所在的目录
script_dir = os.path.dirname(os.path.abspath(__file__))
# 改变当前工作目录
os.chdir(script_dir)

jsDir = "./loadable/"

# 所有关卡文件的信息，key为关卡文件名，value是一个dict，包含关卡文件的信息
lvFileInfo = {}
templateInfo = {}    # 模版信息

# 所有关卡的信息，key为关卡id，value是一个dict，包含关卡的信息
lvInfo = {}

def parseLvid():
    global lvFileInfo, lvInfo
    with open("../config_level.csv", 'r') as lcf:
        # csv 文件，格式如下：
        # LevelId,Des,TemplateId,RandomType,DupPara1,DupPara2,DupPara3,DupPara4,DupPara5,DupPara6,DupPara7,DupPara8,DupPara9,DupPara10
        # string,string,string,int,array,array,array,array,array,array,array,array,array,array
        # 关卡 id,备注,模板 id,0=固定按第n次玩来取副本，1=随机取,第1次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第2次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第3次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第4次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第5次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第6次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第7次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第8次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第9次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第10次及之后玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数)
        # toolguide01,,undo01,0,1|0|100|1,1|0|100|1,1|0|100|1,1|0|100|1,1|0|100|1,1|0|100|1,1|0|100|1,1|0|100|1,1|0|100|1,1|0|100|1
        # 将 csv 文件转为 dict，存放到 lvFileInfo 中；忽略前两行，并将第3行作为 dict 的 key，后面的行作为 value
        lcf.readline()
        lcf.readline()
        keys = lcf.readline().strip().split(',')
        for line in lcf:
            line = line.strip()
            if len(line) == 0:
                continue
            if line[0] == '#':
                continue
            line = line.split(',')
            lvFileInfo[line[0]] = dict(zip(keys, line))
    with open("../config_level_list.csv") as cllf:
        # csv 文件，格式如下：
        # LevelIndex,LevelType,LevelCoin,RewardCoinMultiple,LevelId,DDALevelId1,DDALevelId2,DDALevelId3,DDAType,DDAMatrix,LevelDuration,LevelCoinPercentage
        # int,int,int,int,array,array,array,array,int,array,int,int
        # 第几关,关卡类型，0=普通，1=Hard，2=SuperHard，3=金币,基础金币奖励值。结算金币数=0时不显示。,用户看完广告后获得的金币倍数,关卡 id,关卡 id,关卡 id,关卡 id,DDA 类型，0=不走DDA（取 LevelId 列），1=走DDA，2=考试关（取 LevelId 列）,DDA 参数,金币关的关卡时长(单位秒)，最高100分钟，即6000s,金币tile的比例（单位为%，取值0-100）
        # 1,0,0,0,tempnm0017,tempnm0017,tempnm0017,tempnm0017,0,0|100,,
        # 将 csv 文件转为 dict，存放到 lvInfo 中；忽略前两行，并将第3行作为 dict 的 key，后面的行作为 value
        cllf.readline()
        cllf.readline()
        keys = cllf.readline().strip().split(',')
        for line in cllf:
            line = line.strip()
            if len(line) == 0:
                continue
            if line[0] == '#':
                continue
            line = line.split(',')
            lvInfo[line[0]] = dict(zip(keys, line))
    pass

# 解析关卡的种子信息
lvSeedInfo = {}
def parseSeed():
    global lvSeedInfo
    with open("../lv_seed.txt") as cllf:
        # TileManor输出的文件
        # 每一行的格式：[Tile] resovler id: 30,735680|69576|719549|907289|550748|399652|246018|611094|979453|303995|269635|
        # 去掉头部："[Tile] resovler id: " 之后的数据分成两部分，第一部分是lvid，后面是一系列种子的列表
        # 将这些数据存放到 lvSeedInfo 中，key为lvid，value为种子列表
        for line in cllf:
            line = line.strip()
            if len(line) == 0:
                continue
            if line[0] == '#':
                continue
            fs = line.split(':')[1].split(',')
            lvid = fs[0].strip()
            seeds = fs[1].split('|')
            lvSeedInfo[lvid] = seeds
    pass

def converJosnToProto(tDir, tFileName, leveId):
    global lvFileInfo, lvInfo
    jsonFile = os.path.join(tDir, tFileName + '.json')

    with open(jsonFile, 'r') as f:#, encoding='utf-8'
        jsonData = json.load(f)

    levelData = levelData_pb2.LevelData()

    levelData.tileWidth = jsonData['LevelWidth']
    levelData.tileHeight = jsonData['LevelHeight']
    cntUndefined = 0
    tilesByPos = {}
    maxZ = 0
    maxX = 0
    maxY = 0
    for item in jsonData['LevelData']:
        tile = levelData.tiles.add()
        tile.x = int(item['X'])
        tile.y = int(item['Y'])
        tile.z = int(item['Z'])
        tiledata = tile.tileData
        tiledata.zv = 0
        tiledata.weight = 0
        tiledata.id = int(item['TileId'])
        if tiledata.id == -10:
            cntUndefined += 1
        tiledata.type = 1
        tiledata.subtype = 0
        # 一些统计信息
        tilesByPos[(tile.x, tile.y, tile.z)] = tile
        if tile.z > maxZ:
            maxZ = tile.z
        if tile.x > maxX:
            maxX = tile.x
        if tile.y > maxY:
            maxY = tile.y
    # 根据上面的布局信息，计算每个tile的几个信息：
    # 1. 每个tile的视觉层级（不同于上面z的信息，那是一个布局信息，相同的z可能出在不同的视觉层级）
    # 从maxZ开始，逐层向下计算；对于每一个位置，如果该位置有tile，则计算该tile的视觉层级
    # 对于每一个tile，如果其上方（从该tile的z+1层，一直到maxZ层）有tile，则其视觉层级为上方tile的视觉层级+1
    for z in range(0, maxZ+1):
        z = maxZ - z
        for x in range(0, maxX+1):
            for y in range(0, maxY+1):
                if (x,y,z) in tilesByPos:
                    tile = tilesByPos[(x, y, z)]
                    adjs = [(x,y),(x,y+1),(x,y-1),(x+1,y),(x+1,y+1),(x+1,y-1),(x-1,y),(x-1,y-1),(x-1,y+1)]
                    # 确定视觉层级
                    zvMax = -1
                    for zup in range(z+1, maxZ+1):
                        for adj in adjs:
                            if (adj[0],adj[1],zup) in tilesByPos:
                                zv = tilesByPos[adj[0],adj[1],zup].tileData.zv
                                if zv > zvMax:
                                    zvMax = zv
                    tile.tileData.zv = zvMax + 1
    # 计算权重
    # 从最底下开始，逐层向上计算
    # 每个tile的初始权重为4，然后加上其下方压住的tile的权重：如果压了全部，则该加上被压住的tile权重；
    # 如果压住了一半，则加上被压住的tile权重的一半；如果是压住1/4，则加上被压住的tile权重的1/4
    for z in range(0, maxZ+1): 
        for x in range(0, maxX+1):
            for y in range(0, maxY+1):
                if (x,y,z) in tilesByPos:
                    tile = tilesByPos[(x,y,z)]
                    adjs = [(x,y,1.0),(x,y+1,0.5),(x,y-1,0.5),(x+1,y,0.5),(x+1,y+1,0.25),(x+1,y-1,0.25),(x-1,y,0.5),(x-1,y-1,0.25),(x-1,y+1,0.25)]
                    weight = 4
                    for adj in adjs:
                        # 从临近层往下，在某个位置找到了tile，则该tile被压住了，就不再找了
                        for zb in range(0, z):
                            zb = z-1-zb
                            if (adj[0], adj[1], zb) in tilesByPos:
                                tileB = tilesByPos[(adj[0], adj[1], zb)]
                                weight += tileB.tileData.weight * adj[2]
                                break
                    tile.tileData.weight = int(weight)
        pass

    for item in jsonData['StackData']:
        stack_data = levelData.stacks.add()
        stack_data.x = int(item['X'])
        stack_data.y = int(item['Y'])
        stack_data.direction = int(item['Direction'])

    templateInfo[tFileName] = {}
    if cntUndefined == 0:
        templateInfo[tFileName]['无未定义的tile'] = '1'
    elif cntUndefined == len(jsonData['LevelData']):
        templateInfo[tFileName]['所有tile都未被定义'] = '1'
    else:
        templateInfo[tFileName]['部分tile被定义'] = '1'
    if len(jsonData['StackData']) > 0:
        templateInfo[tFileName]['有stack数据'] = '1'
    
    # 数据序列化
    level_protobuf_data = levelData.SerializeToString()
    protoFile = os.path.join(jsDir, tFileName + '.bin')
    with open(protoFile, 'w+b') as f:
        f.write(level_protobuf_data)

#关卡数据拼接map+trcuk
def mergeLeveldata3(group = None):
    lvelsIndex = levelData_pb2.LevelsIndex()
    if group is not None:
        protoDataTrunk = os.path.join(jsDir, 'levels-' + str(group) + '.bin')
        protoDataIndex = os.path.join(jsDir, 'levelsIndex-' + str(group) + '.bin')
    else:
        protoDataTrunk = os.path.join(jsDir, 'levels' + '.bin')
        protoDataIndex = os.path.join(jsDir, 'levelsIndex' + '.bin')

    offset = 0
    for filename in os.listdir(jsDir):
        my_message = levelData_pb2.LevelData()
        binFile = os.path.splitext(filename)[0] + '.bin'
        binFile = os.path.join(jsDir, binFile)
        if os.path.exists(binFile) :    
            # print(binFile)        
            with open(binFile, "rb") as f:
                binary_data = f.read()
                my_message.ParseFromString(binary_data)
            with open(protoDataTrunk, 'ab') as f:
                f.write(binary_data)
            lvelsIndex.LevelsIndex[os.path.splitext(filename)[0]].len = len(binary_data)
            lvelsIndex.LevelsIndex[os.path.splitext(filename)[0]].offset = offset
            offset += len(binary_data)
        
    level_protobuf_index = lvelsIndex.SerializePartialToString()
    with open(protoDataIndex, 'w+b') as f:
        f.write(level_protobuf_index)

if __name__ == '__main__':
    parseLvid()
    parseSeed()

    folder_path = '../level_data2'
    for filename in os.listdir(folder_path):
        # 获取文件的绝对路径
        file_path = os.path.join(folder_path, filename)

        # 检查是否是文件而不是文件夹
        if os.path.isfile(file_path):
            base_name = os.path.splitext(filename)
            # 在文件转为bin
            converJosnToProto(folder_path, base_name[0],1)
    mergeLeveldata3()
            
    # 将关卡id和模板的对应关系输出到文件
    lv_stat = {}
    got = False
    for k, v in lvInfo.items():
        levelId = v["关卡 id"]
        templateId = lvFileInfo[levelId]["模板 id"]
        if templateId not in templateInfo:
            templateId += '_m'
        if templateId in templateInfo:
            # 将所有信息综合到一块
            lv_stat[k] = lvFileInfo[levelId].copy()
            for key in templateInfo[templateId].keys():
                lv_stat[k][key] = templateInfo[templateId][key]
            lv_stat[k]["关卡 id"] = levelId
            lv_stat[k]["关卡类型"] = v["关卡类型，0=普通，1=Hard，2=SuperHard，3=金币"]
            lv_stat[k]["dda_type"] = v["DDA 类型，0=不走DDA（取 LevelId 列），1=走DDA，2=考试关（取 LevelId 列）"]
            lv_stat[k]["dda_para"] = v["DDA 参数"]
            lv_stat[k]["模板 id"] = templateId
    # 关卡与模版的对应关系，以及关卡本身的一些控制参数
    with open("levelInfo.csv", 'w+') as f:
        fieldnames = ["lvid","lvinst","type(normal=0,hard=1,superHard=2)","dda_type","dda_para"]
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        for lvid in sorted(lv_stat.keys(), key=lambda x:int(x)):
            d = {"lvid":lvid, "lvinst":"","type(normal=0,hard=1,superHard=2)":"","dda_type":"","dda_para":""}
            d["lvinst"] = lv_stat[lvid]["关卡 id"]
            d["type(normal=0,hard=1,superHard=2)"] = lv_stat[lvid]["关卡类型"]
            d["dda_type"] = lv_stat[lvid]["dda_type"]
            d["dda_para"] = lv_stat[lvid]["dda_para"]
            writer.writerow(d)
    # 模版的信息
    with open("levelInstInfo.csv", 'w+') as f:
        all_insts = set()
        fieldnames = ["lvinst","template",
                      "try1(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
                      "try2(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
                      "try3(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
                      "try4(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
                      "try5(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
                      "try6(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
                      "try7(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
                      "try8(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
                      "try9(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
                      "try10(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)"]
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        for lvid in sorted(lv_stat.keys(), key=lambda x:int(x)):
            inst_id = lv_stat[lvid]["关卡 id"]
            if inst_id not in all_insts:
                all_insts.add(inst_id)
            else:
                continue
            d = {"lvinst":"", "template":"",
                  "try1(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
                  "try2(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
                  "try3(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
                  "try4(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
                  "try5(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
                  "try6(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
                  "try7(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
                  "try8(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
                  "try9(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
                  "try10(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":""}
            d["lvinst"] = lv_stat[lvid]["关卡 id"]
            d["template"] = lv_stat[lvid]["模板 id"]
            seeds = []
            if lvid in lvSeedInfo:
                seeds = lvSeedInfo[lvid]
            for tried in range(1,11):
                k1 = "第" + str(tried) + "次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数)"
                if tried == 10:
                    k1 = "第10次及之后玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数)"
                if k1 in lv_stat[lvid]:
                    try1 = lv_stat[lvid][k1]
                    tileA = try1.split('|')[0]
                    tileB = try1.split('|')[3]
                    tileCnt = tileA if int(tileA) > int(tileB) else tileB
                    ps=[tileCnt,"0","0","100","100"]
                    if tried <= len(seeds):
                        ps.append(seeds[tried-1])
                    else:
                        ps.append("100")
                    d["try" + str(tried)+"(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)"] = "|".join(ps)
                else:
                    d["try" + str(tried)+"(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)"] = ""
            writer.writerow(d)


    # 导出关卡id和模板的对应关系，并输出模板的信息
else:
    print('ohoh')



# 恢复原来的工作目录
os.chdir(current_dir)