import hashlib
import sys
import os
import re
import time
import json
import functools
import shutil
import inspect

DEBUG_MODE = False  # 打开后不会修改文件，只读文件
USE_BACKUP = False

LOGTAG = "[findVersion]"

#
def join2Abs(path, *paths):
	return os.path.abspath(os.path.join(path, *paths))

# 读文件
def readFile(file):
    f = open(file, "r")
    content = f.read()
    f.close()
    return content

# 写文件
def writeFile(file, content):
    f = open(file, "w")
    f.write(content)
    f.close()

# 把json写到文件中
def writeJsonToFile(file, jsonContent):
    writeFile(file, json.dumps(jsonContent, indent="\t", sort_keys=True))

# 获取文件的md5
def getFileMd5(file):
	with open(file, 'rb') as f:
		return hashlib.md5(f.read()).hexdigest()

# 将文件递归移动到目标
def moveTree(src, dst):
    shutil.move(src, dst)

# 复制
def copyTree(src, dst):
	shutil.copytree(src, dst, True)

# 递归删除
def rmTree(dir):
	shutil.rmtree(dir, True)

# 比较是否小于安装的app版本
def cmp_less_equ_app_version(l, r):
    splits_l = l.split(".")
    splits_r = r.split(".")
    for i in range(0, 3):
        n_l = int(splits_l[i])
        n_r = int(splits_r[i])
        if n_l < n_r:
            return True
        elif n_l > n_r:
            return False
    return True

# 比较是否大于安装的app版本
def cmp_greater_equ_app_version(l, r):
    splits_l = l.split(".")
    splits_r = r.split(".")
    for i in range(0, 3):
        n_l = int(splits_l[i])
        n_r = int(splits_r[i])
        if n_l > n_r:
            return True
        elif n_l < n_r:
            return False
    return True

# 按照app版本排序
def sort_by_app_version(l, r):
    l = l["app_version"]
    r = r["app_version"]
    splits_l = l.split(".")
    splits_r = r.split(".")
    for i in range(0, 3):
        n_l = int(splits_l[i])
        n_r = int(splits_r[i])
        d = n_l - n_r
        if d != 0:
            return -d
    return 0

#rootPath:http://192.144.239.125:55068/updatefiles/
def main(rootPath):
    print(LOGTAG,"platform%s: ", sys.platform)
    # ab清单文件的路径
    sublistsDir = os.path.join(rootPath, "sublists")
    # ab路径
    modulesDir = os.path.join(rootPath, "modules")

    platformName = os.path.basename(rootPath)
    backUpDir = os.path.join(rootPath, "..", "backup", platformName)
    sublistsBackUpDir = os.path.join(backUpDir, "sublists")
    modulesBackUpDir = os.path.join(backUpDir, "modules")

    all_versions = [] #所有的大版本号 e.g: 1.0.0 2.0.0 1.1.2 1.1.3等等
    all_branchNames = [] #所有的分支名
    abTypes = []#所有的ab类型:inpkg_src, inpkg_res

    for abType in os.listdir(sublistsDir):
        typeDir = join2Abs(sublistsDir, abType)
        appVersionsDir = join2Abs(typeDir, "app_versions")
        if os.path.isdir(appVersionsDir):
            versions = []
            one_version = {
            "abType": abType,
            "app_versions": versions,
            }
            abTypes.append(one_version)

            for appVersion in os.listdir(appVersionsDir):
                versionDir = join2Abs(appVersionsDir, appVersion)
                branchMap = {}
                versions.append({
					"app_version": appVersion,
					"branchMap": branchMap,
				})
                # 遍历每个版本的分支，找到最新的svn revision，hash，写入latest_revision.txt方便后续读取
                for branchName in os.listdir(versionDir):
                    branchDir = join2Abs(versionDir, branchName) # 每个版本的分支目录 e.g: 1.0.0/branchName
                    latestRevisionFile = join2Abs(branchDir, "latest_revision.txt") #每个版本的分支目录下的latest_revision.txt
                    if not branchName in all_branchNames: # 所有的分支名,用于生成all_summary.json
                        all_branchNames.append(branchName)
                    if os.path.isfile(latestRevisionFile): # 如果latest_revision.txt存在，说明这个分支有资源更新，需要记录，用于生成summary.json
                        if not appVersion in all_versions: 
                            all_versions.append(appVersion)
                        LastestRevisionContent = readFile(latestRevisionFile) 
                        splits = LastestRevisionContent.split(",") # latest_revision.txt内容格式:revision,hash
                        LastestRevision = splits[0]
                        if len(splits) > 1:
                            LastestHash = splits[1]
                        else:
                            LastestHash = ""
                        branchMap[branchName] = {
							"app_version": appVersion,
							"lastest_revision": LastestRevision,
							"branchName": branchName,
							"lastest_hash": LastestHash,
						}
    print(LOGTAG, "collect versions")

    #为每个app_version(1.0.0, 2.0.0 1.1.2 1.1.3等等)找到对应 typeName 的小于当前app_version且最大app_version的svn revision(latest_revision)
	#小于当前且最大满足:一个类型typeName在比较新的版本比如9.0.0,但是9.0.0上并没有对typeName的修改，上次修改在5.0.0，那就应该使用5.0.0的资源
    branch_version_map = {}
    for branchName in all_branchNames:
        version_revision_map = {}
        branch_version_map[branchName] = version_revision_map
        for target_app_version in all_versions:
            version_revision_map[target_app_version] = {}
            for one_abType in abTypes:
                app_versions = one_abType["app_versions"]
                try:
                    app_versions = sorted(
                        app_versions, key=functools.cmp_to_key(sort_by_app_version))
                except Exception as e:
                    print(e)
                    print("[Error]sort ab type error abType:[%s]" %
                          (one_abType))
                    sys.exit(1)

                for iVersion in range(len(app_versions)):
                    one_version = app_versions[iVersion]
                    try:
                        if cmp_less_equ_app_version(one_version["app_version"], target_app_version):
                            branchMap = one_version["branchMap"]
                            if branchName in branchMap:
                                version_revision_map[target_app_version][one_abType["abType"]] = {
                                "revision": branchMap[branchName]["lastest_revision"],
                                "hash": branchMap[branchName]["lastest_hash"],
                                "branchName": branchName,
                                }
                                break
                            else:
                                 if "client_project" in branchMap:
                                    version_revision_map[target_app_version][one_abType["abType"]] = {
                                        "revision": branchMap["client_project"]["lastest_revision"],
                                        "hash": branchMap["client_project"]["lastest_hash"],
                                        "branchName": "client_project",
                                        }

                    except Exception as e:
                        print(e)
                        print("[Error]cmp ab type error abType:[%s],version:[%s]" % (
                            one_abType, one_version))
                        sys.exit(1)
    versionsDir = join2Abs(rootPath, "versions")
    if not os.path.isdir(versionsDir):
        os.makedirs(versionsDir)

    print(LOGTAG, "compare versions")

    typeUsedRevisionsMap = {}
    #生成每个app_version的每个类型对应的latest_revision文件。方便每次进游戏前检查热更(文件小，可以每次都下载)。
    for branchName in branch_version_map:
        print(LOGTAG, "branchName: %s" % branchName)
        version_revision_map = branch_version_map[branchName]
        for app_version in version_revision_map:
            print(LOGTAG, "app_version %s" % app_version)
            one_version_dir = join2Abs(versionsDir, branchName, app_version)
            if not os.path.isdir(one_version_dir):
                os.makedirs(one_version_dir)

            one = version_revision_map[app_version]
            summary = {}
            for assetType in one:
                print(LOGTAG, "assetType %s" % assetType)
                svn_revision = one[assetType]["revision"]
                real_branchName = one[assetType]["branchName"]
                cfg_hash = one[assetType]["hash"]
                if cfg_hash == "":  # 兼容老代码
                    print(LOGTAG, "old code: %s-%s-%s read .json.hash" %
                          (assetType, branchName, app_version))
                    sublistFile = join2Abs(
                        rootPath, "sublists/%s/%s/sublist/%s.json.hash" % (assetType, svn_revision, assetType))
                    sublistMd5 = readFile(sublistFile)
                else:
                    print(LOGTAG, "new code: %s-%s-%s read hash in lasest_revision.txt" %
                          (assetType, branchName, app_version))
                    sublistMd5 = cfg_hash

                summary[assetType] = {
                    "revision": svn_revision,
                    "md5": sublistMd5,
                    "branchName": real_branchName,
                }

                print(LOGTAG, "\t[branchName: %s, svn_revision: %s]\tassetType: %s\tsublistMd5: %s" %
                      (real_branchName, svn_revision, assetType, sublistMd5))

            summary["__appVersion__"] = app_version
            summary["__branchName__"] = branchName
            summaryFile = join2Abs(one_version_dir, "summary.json")
            writeJsonToFile(summaryFile, summary)
            summaryHashFile = summaryFile + ".hash"
            writeFile(summaryHashFile, getFileMd5(summaryFile))

            for abType in one:
                revision = one[abType]['revision']
                if not abType in typeUsedRevisionsMap:
                    typeUsedRevisionsMap[abType] = {}

                typeUsedRevisionsMap[abType][revision] = True
    print(LOGTAG, "write each summary")

    #生成all_summary.json
    allSummaryFile = join2Abs(versionsDir, "all_summary.json")
    writeJsonToFile(allSummaryFile, branch_version_map)
    print(LOGTAG, "write all_summary")

    #删除没用的资源，放到backup dir(为了加快打包速度，尽量关闭backup机制!!!!!!!!!)
    for abType in typeUsedRevisionsMap.keys():
        typeRevisionsMap = typeUsedRevisionsMap[abType]
        typeSublistsDir = join2Abs(sublistsDir, abType)
        typeModulesDir = join2Abs(modulesDir, abType)
        backUpSublistsDir = join2Abs(sublistsBackUpDir, abType)
        backUpModulesDir = join2Abs(modulesBackUpDir, abType)

        keepRes = None
        #删除sublists
        for revision in os.listdir(typeSublistsDir):
                if keepRes != None:
                    if revision in keepRes:
                        print(LOGTAG, "sublist %s %s keeped!" % (abType, revision))
                        continue
                if not revision in typeRevisionsMap:
                    subDir = join2Abs(typeSublistsDir, revision)
                    backUpDir = join2Abs(backUpSublistsDir, revision)
                    if not DEBUG_MODE:
                        if revision == "app_versions":
                            if os.path.exists(backUpDir):
                                rmTree(backUpDir)
                            if USE_BACKUP:
                                copyTree(subDir, backUpDir)
                        else:
                            if os.path.isdir(backUpDir):
                                rmTree(subDir)
                            else:
                                if USE_BACKUP:
                                    moveTree(subDir, backUpDir)
                                    
        #删除modules
        for revision in os.listdir(typeModulesDir):
            if keepRes != None:
                if revision in keepRes:
                    print(LOGTAG, "sublist %s %s keeped!" % (abType, revision))
                    continue
            if not revision in typeRevisionsMap and revision != "app_versions":
                subDir = join2Abs(typeModulesDir, revision)
                backUpDir = join2Abs(backUpModulesDir, revision)
                if not DEBUG_MODE:
                    if os.path.isdir(backUpDir):
                        rmTree(subDir)
                    else:
                        if USE_BACKUP:
                            moveTree(subDir, backUpDir)

        #backup modules目录只保存最多5个
        if USE_BACKUP:
            if os.path.isdir(backUpModulesDir):
                lists = os.listdir(backUpModulesDir)
                lists.sort()
                needDelNum = len(lists) - 5 - 1
                if needDelNum > 0:
                    delNum = 0
                    for dirName in lists:
                        if dirName == "app_versions":
                            continue
                        if delNum >= needDelNum:
                            break
                        delDir = join2Abs(backUpModulesDir, dirName)
                        if not DEBUG_MODE:
                            rmTree(delDir)
                        delNum = delNum + 1
                        
    print(LOGTAG, "delete and backup unused files")

    print(LOGTAG, "finished!")
                            
	
    

if __name__ == "__main__":
    rootPath = sys.argv[1]
    # rootPath = "/Users/zhangpeng/xgame/client/xClient/Project/client_project/Tools/find_versions/dev/android/"
    main(rootPath)