import configparser
import winreg
import subprocess
import os.path
import json
import shutil
import hashlib
import glob

myConfigparser = configparser.ConfigParser()

def InitConfigParser(iniFilepath):
    myConfigparser.read(iniFilepath)

def GetEngineRootDir():
    return os.path.normpath(myConfigparser['EnvironmentConfig']['EngineRootDir'])

def GetUnrealPakToolPath():
    return os.path.normpath(GetEngineRootDir() + "\\Engine\\Binaries\\Win64\\UnrealPak.exe")

def GetTargetPorjectRootDir():
    return os.path.normpath(myConfigparser['TargetProjectConfig']['ProjectRootDir'])

def GetTargetPorjectName():
    _, porjectName = os.path.split(GetTargetPorjectRootDir())
    return porjectName

def GetTargetProjectCookedGameDir():
    return os.path.normpath(os.path.join(GetTargetPorjectRootDir(), "Saved\\Cooked\\WindowsNoEditor", GetTargetPorjectName()))

def GetTargetProjectCookedRootDir():
    return os.path.normpath(os.path.join(GetTargetPorjectRootDir(), "Saved\\Cooked\\WindowsNoEditor"))

def GetTargetProjectCookedContentDir():
    return os.path.normpath(os.path.join(GetTargetPorjectRootDir(), "Saved\\Cooked\\WindowsNoEditor", GetTargetPorjectName(), "Content" ))

def GetSha1OfLongPackageName(longPackageName):
    sha1Obj = hashlib.sha1()
    sha1Obj.update(str.encode(longPackageName))
    return sha1Obj.hexdigest()

def GeneratePak(outputPakFileDir, targetPackagePathRootDir, longPackageName, inShouldPackTo64KB = False):
    pakCmdTemplate = '"{}" "{}" -create="{}" -encryptionini -platform=Windows -installed -UTF8Output -multiprocess -patchpaddingalign=2048 -abslog="{}" -projectdir={} -enginedir={}'
    urealPakToolPath = GetUnrealPakToolPath()
    outputPakFileDir = os.path.normpath(outputPakFileDir)
    targetPackagePathRootDir = os.path.normpath(targetPackagePathRootDir)
    outputPakFilename = GetSha1OfLongPackageName(longPackageName)
    # Generate pak response file.
    pakReponseFilepath = os.path.join(GetTargetPorjectRootDir(), "Saved\\ProjectUtils\\paklist.txt")
    with open(pakReponseFilepath, 'w') as pakReponseFileHandle:
        filesToWrite = []
        fileToWriteEntry = longPackageName.replace('/Game', "Content" )
        fileToWriteEntry = os.path.normpath(os.path.join(targetPackagePathRootDir , fileToWriteEntry))
        searchStr = '{}.*'.format(fileToWriteEntry)
        searchResult = glob.glob(searchStr)
        rootProjectDirInSavedContent = GetTargetPorjectRootDir
        for r in searchResult:
            # Convert to relative
            tt = GetTargetProjectCookedRootDir()
            relativePath = r.replace(tt, "..\\..\\..")
            relativePath = relativePath.replace("\\", "/")
            filesToWrite.append('"{}" "{}"\n'.format(r, relativePath))

        # Add dummy file
        # dummyPackagePath = os.path.join(targetPackagePathRootDir, "dummy.uasset")  
        # filesToWrite.append('"{}"\n'.format(dummyPackagePath))

        # Check pack
        packDataFilepath = None
        if inShouldPackTo64KB:
            targetProjectCookedContentDir = GetTargetProjectCookedContentDir()
            packDataFilepath = os.path.join(targetProjectCookedContentDir, "pack_data.dat")
            if not os.path.exists(packDataFilepath):
                shutil.copy('.\\pack_data.dat', targetProjectCookedContentDir)
                
        if packDataFilepath != None:
            filesToWrite.append('"{}" "{}"\n'.format(packDataFilepath, "../../../" + GetTargetPorjectName()  + "/Content/pack_data.dat"))
    
        pakReponseFileHandle.writelines(filesToWrite)

    absPakLogFilepath = os.path.join(GetTargetPorjectRootDir(), "Saved\\ProjectUtils\\Log\\", outputPakFilename) + ".log"
      
    outputPakFilePath = os.path.join(outputPakFileDir, outputPakFilename + ".pak")

    projectDir = GetTargetPorjectRootDir()
    engineDir = GetEngineRootDir()

    pakCmd = pakCmdTemplate.format(urealPakToolPath, outputPakFilePath, pakReponseFilepath, absPakLogFilepath, projectDir, engineDir)

    subprocess.check_call(pakCmd, shell=True)

    return outputPakFilePath

def GenerateBatchPak(outputPakFileDir, targetPackagePathRootDir, longPackageNameList, outputPakFilename, inShouldPackTo64KB = False):
    pakCmdTemplate = '"{}" "{}" -create="{}" -encryptionini -platform=Windows -installed -UTF8Output -multiprocess -patchpaddingalign=2048 -abslog="{}"'
    urealPakToolPath = GetUnrealPakToolPath()

    outputPakFileDir = os.path.normpath(outputPakFileDir)
    targetPackagePathRootDir = os.path.normpath(targetPackagePathRootDir)
   
    # Generate pak response file.
    pakReponseFilepath = os.path.join(GetTargetPorjectRootDir(), "Saved\\ProjectUtils\\paklist.txt")
    with open(pakReponseFilepath, 'w') as pakReponseFileHandle:
        filesToWrite = []
        for longPackageName in longPackageNameList:
            fileToWriteEntry = longPackageName.replace('/Game', "Content" )
            fileToWriteEntry = os.path.normpath(os.path.join(targetPackagePathRootDir , fileToWriteEntry))
            searchStr = '{}.*'.format(fileToWriteEntry)
            searchResult = glob.glob(searchStr)
            rootProjectDirInSavedContent = GetTargetPorjectRootDir
            for r in searchResult:
                # Convert to relative
                tt = GetTargetProjectCookedRootDir()
                relativePath = r.replace(tt, "..\\..\\..")
                relativePath = relativePath.replace("\\", "/")
                filesToWrite.append('"{}" "{}"\n'.format(r, relativePath))

        # Add dummy file
        # dummyPackagePath = os.path.join(targetPackagePathRootDir, "dummy.uasset")  
        # filesToWrite.append('"{}"\n'.format(dummyPackagePath))

        # Check pack
        packDataFilepath = None
        if inShouldPackTo64KB:
            packDataFilepath = os.path.join(targetPackagePathRootDir, "pack_data.dat")
            if not os.path.exists(packDataFilepath):
                shutil.copy('.\\pack_data.dat', targetPackagePathRootDir)
            packDataFilepath = os.path.join(targetPackagePathRootDir, "pack_data.dat")
   
        if packDataFilepath != None:
            filesToWrite.append('"{}" "{}"\n'.format(packDataFilepath, "../../../" + GetTargetPorjectName()  + "/Content/pack_data.dat"))
    
        pakReponseFileHandle.writelines(filesToWrite)

    absPakLogFilepath = os.path.join(GetTargetPorjectRootDir(), "Saved\\ProjectUtils\\Log\\", outputPakFilename) + ".log"
      
    outputPakFilePath = os.path.join(outputPakFileDir, outputPakFilename + ".pak")

    pakCmd = pakCmdTemplate.format(urealPakToolPath, outputPakFilePath, pakReponseFilepath, absPakLogFilepath)

    subprocess.check_call(pakCmd, shell=True)

    return outputPakFilePath



if __name__ == "__main__":
    
    InitConfigParser("ProjectUtilsConfig.ini")    
    
    #Delete old project utils saved dir if exists.
    projectUtilsSavedDir =  os.path.join(GetTargetPorjectRootDir(), "Saved\\ProjectUtils\\")
    if os.path.exists(projectUtilsSavedDir):
        subprocess.check_call("rd /q /s {}".format(projectUtilsSavedDir), shell = True)
    
    subprocess.check_call("md {}".format(projectUtilsSavedDir), shell = True)

    generatePakLogFilepath = os.path.join(projectUtilsSavedDir, "GeneratePaks.log")
    assetDependenciesFileFilpath = os.path.join(GetTargetPorjectRootDir(), "Saved\\ExportAssetDependecies\\AssetDependencies.json")

    targetProjectCookedGameDir = GetTargetProjectCookedGameDir()

    with open(generatePakLogFilepath, 'w') as generatePakLogFileHandle:
        with open(assetDependenciesFileFilpath, 'r') as assetDependenciesFileHandle:
            data = json.load(assetDependenciesFileHandle)
            for (targetPackage,dependentData) in  data.items():
                sha1OfLongTargetPackageName = GetSha1OfLongPackageName(targetPackage)

                # Now just handle dependencies in game content dir.
                packagesToHandle = dependentData['DependenciesInGameContentDir']
                packagesToHandle.append(targetPackage)

                # Make output dir with sha1 value 
                targetOutputDir = os.path.join(projectUtilsSavedDir, sha1OfLongTargetPackageName)
                os.mkdir(targetOutputDir)

                # Fill json object and generate pak.
                jsonObject = {}
                jsonObject["long_package_name"] = targetPackage
                jsonObject["dependent_pak_file_list"] = []

                bShouldGenerateBatchPak = myConfigparser.getboolean("TargetProjectConfig", "UseBatchPakFile")
                if not bShouldGenerateBatchPak:
                    for longPackageName in packagesToHandle:
                        outputPakFilePath = GeneratePak(targetOutputDir, targetProjectCookedGameDir, longPackageName)
                        _, outputFilename = os.path.split(outputPakFilePath) 

                        if longPackageName != targetPackage:
                            #  
                            entry = {}
                            entry["file_name"] = outputFilename
                            entry["file_size"] = os.path.getsize(outputPakFilePath)
                            entry["long_package_name"] = longPackageName
                            jsonObject["dependent_pak_file_list"].append(entry)
                        else:
                            # Dependencies
                            filename = longPackageName.replace('/Game', "" )
                            filename = os.path.normpath(targetProjectCookedGameDir + filename)
                            jsonObject["file_size"] = os.path.getsize(outputPakFilePath)
                            jsonObject["pak_file"] = outputFilename
                            jsonObject["asset_class"] = dependentData["AssetClass"]

                            # Write json object to info file.
                            generatePakLogFileHandle.write('"{}" --> "{}"\n'.format(longPackageName, sha1OfLongTargetPackageName ))
                else:
                    outputPakFilePath = GenerateBatchPak(targetOutputDir, targetProjectCookedGameDir, packagesToHandle, sha1OfLongTargetPackageName)
                    _, outputFilename = os.path.split(outputPakFilePath) 
                    
                    # Dependencies
                    filename = targetPackage.replace('/Game', "content" )
                    filename = os.path.normpath(os.path.join(targetProjectCookedGameDir,filename))
                    jsonObject["file_size"] = os.path.getsize(outputPakFilePath)
                    jsonObject["pak_file"] = outputFilename
                    jsonObject["asset_class"] = dependentData["AssetClass"]

                    # Write json object to info file.
                    generatePakLogFileHandle.write('"{}" --> "{}"\n'.format(targetPackage, sha1OfLongTargetPackageName ))

                outputInfoFilepath = os.path.join(targetOutputDir, '{}.json'.format(sha1OfLongTargetPackageName))
                with open(outputInfoFilepath, 'w') as outputInfoFileHandle:
                    outputInfoFileHandle.write(json.dumps(jsonObject, indent=4))

    # Explore to projectUtilsSavedDir for convenience.
    os.system("explorer.exe %s" % projectUtilsSavedDir)
                    