import os
import os.path
import shutil
import subprocess
import platform
import sys
import hashlib

def getFullToolPath(name):
    '''
    获取工具的目录
    '''
    return getFullPath('tools', name)

def getFullGameApk(name):
    '''
    获取游戏的原始包
    '''
    return getFullPath('game', name, name + '.apk')

def getFullSDKPath(sdk):
    '''
    获取sdk的目录
    '''
    return getFullPath('sdk', sdk)

def getFullLogSDKPath(sdk):
    '''
    获取logsdk的目录
    '''
    return getFullPath('log_sdk', sdk)

def getDecompliePath(game, sdk, subChannel, cache):
    '''
    获取解包的目录
    '''
    return getFullPath('gen', game, sdk, subChannel, cache)

def getSubChannelPath(game, sdk, subChannel):
    '''
    获取子渠道的目录
    '''
    return getFullPath('game', game, sdk, subChannel)

def getChannelPath(game, sdk):
    '''
    获取渠道的目录
    '''
    return getFullPath('game', game, sdk)

def getFullGamePath(game):
    '''
    获取游戏的目录
    '''
    return getFullPath('game', game)

def getFullInternalPath():
    '''
    获取内部目录
    '''
    return os.path.join(getCurrentPath(), 'internal')

def getFullPath(type, *name):
    path = os.path.join(getCurrentPath(), type)
    for n in name:
        path = os.path.join(path, str(n))
    return path

def getCurrentPath():
    '''
    当前目录
    '''
    return sys.path[0]

def execFormatCmd(cmd, cd = None):
    '''
    执行cmd命令

    返回值：None —— 子进程尚未结束；
        ==0 —— 子进程正常退出；
        > 0—— 子进程异常退出，returncode对应于出错码；
        < 0—— 子进程被信号杀掉了。
    '''
    '''print(cmd)
    p = os.popen(cmd)
    print(p.read())'''
    ret = 0
    try:
        s = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=cd)
        stdoutput, erroutput = s.communicate()

        if platform.system() == 'Windows':
            stdoutput = stdoutput.decode('gbk')
            erroutput = erroutput.decode('gbk')
            
        '''
        None —— 子进程尚未结束；
        ==0 —— 子进程正常退出；
        > 0—— 子进程异常退出，returncode对应于出错码；
        < 0—— 子进程被信号杀掉了。
        '''
        ret = s.returncode

        if ret:
            print('*******ERROR*******')
            print(stdoutput)
            print(erroutput)
            print('*******************')

            cmd = 'error::' + cmd + '  !!!exec Fail!!!  '
        else:
            print(stdoutput)
            print(erroutput)

            cmd = cmd + ' !!!exec success!!! '

        print(cmd)

    except Exception as e:
        print('Exception ' + e)
        return 1

    return ret

def execJarCmd(jar, params):
    '''
    执行jar
    '''
    return execFormatCmd('java -jar "%s" %s' % (jar, params))

def copyFileAllDir(fromDir, toDir, delete = False, support = None):
    '''
    拷贝目录下所有文件文件
    '''
    #print('copy all file %s --> %s' % (fromDir, toDir))
    ret = copyDir(fromDir, toDir, delete, support)
    if ret:
        return ret

    if delete:
        deleteFolder(fromDir)

    return 0

def copyDir(fromDir, toDir, delete = False, support = None):
    '''
    拷贝目录下所有文件文件
    '''
    #print('copy all file %s --> %s' % (fromDir, toDir))
    if not os.path.exists(fromDir):
        print('%s not exists!' % fromDir)
        return 1
        
    if not os.path.isdir(fromDir):
        print('%s not a dir!' % fromDir)
        return 1

    for fromFile in os.listdir(fromDir):
        fromFilePath = os.path.join(fromDir, fromFile)
        toFilePath = os.path.join(toDir, fromFile)
        if os.path.isdir(fromFilePath):
            # 不支持的类型
            if support is not None and fromFile not in support:
                continue

            ret = copyDir(fromFilePath, toFilePath, delete, support)
            if ret:
                return ret
        else:
            ret = copyFile(fromFilePath, toFilePath, delete)
            if ret:
                return ret

    return 0

def copyFile(formFile, toFile, delete = False):
    '''
    拷贝文件
    '''
    if not os.path.isfile(formFile):
        print('%s not a file!' % formFile)
        return 1

    fpath, fname = os.path.split(toFile)  #分离文件名和路径
    if not os.path.exists(fpath):
        print('%s not exists, crate' % fpath)
        os.makedirs(fpath)              #创建路径

    '''if os.path.exists(toFile) and os.path.getsize(formFile) > 104857600 and equalsFile(formFile, toFile):
        print('skip copy %s --> %s' % (formFile, toFile))
        if delete:
            os.remove(formFile)
            print('remove %s' % formFile)
        return 0'''

    if delete:
        shutil.move(formFile, toFile)      #移动文件
        print('move %s --> %s' % (formFile, toFile))
    else:
        shutil.copyfile(formFile, toFile)      #复制文件
        print('copy %s --> %s' % (formFile, toFile))

    return 0

def replaceContent(file, oldText, newText):
    '''
    全局替换
    '''
    with openFile(file, 'r+') as f:
        t = f.read()
        t = t.replace(oldText, newText)
        #读写偏移位置移到最开始处
        f.seek(0, 0)
        #清空内容
        f.truncate()
        f.write(t)

def readFile(file):
    '''
    读取文件内容
    '''
    content = ''
    with openFile(file, 'r') as f:
        content = f.read()
    return content

def createFile(file, content):
    '''
    创建文件
    '''
    fpath, fname = os.path.split(file)  #分离文件名和路径
    if not os.path.exists(fpath):
        os.makedirs(fpath)

    with openFile(file, 'w') as f:
        f.write(content)
        f.close()

def openFile(file, mode):
    return open(file, mode, encoding='UTF-8')

def deleteFolder(folder):
    '''
    删除目录以及目录下的文件
    '''
    if not os.path.exists(folder):
        return

    print('remove %s ...' % folder)
    shutil.rmtree(folder)
    print('removed %s' % folder)

def getAAPTPath():
    '''
    获取aapt
    '''
    return getToolWithSystem('aapt')

def getAAPT2Path():
    '''
    获取aapt2
    '''
    return getToolWithSystem('aapt2')

def getAndroidCompileToolPath():
    '''
    获取android.jar
    '''
    return getFullToolPath('android.jar')

def getDxPath():
    '''
    获取dx.jar
    '''
    return getFullToolPath('dx.jar')

def getD8Path():
    '''
    获取d8.jar
    '''
    return getFullToolPath('d8.jar')

def getAlignPath():
    '''
    获取zipalign
    '''
    return getToolWithSystem('zipalign')

def getMultiDexPath():
    '''
    获取multidex.jar
    '''
    return getFullToolPath('android-support-multidex.jar')

def getApkToolPath():
    '''
    获取apktool.jar
    '''
    return getFullToolPath('apktool_2.4.0.jar')

def getBaksmaliPath():
    '''
    获取baksmali.jar
    '''
    return getFullToolPath('baksmali-2.3.jar')

def getApksignerPath():
    '''
    获取apksigner.jar
    '''
    return getFullToolPath('apksigner.jar')

def getWallePath():
    '''
    获取walle-cli-all.jar
    '''
    return getFullToolPath('walle-cli-all.jar')

def getOutApkPath(game, sdk, subChannel, cache):
    '''
    获取输出的apk的目录
    '''
    return getFullPath('target', game, sdk, cache, '%s_%s_%s.apk' % (game, sdk, subChannel))

def getAlignApkPath(game, sdk, subChannel, cache):
    '''
    获取输出的apk的目录
    '''
    return getFullPath('target', game, sdk, cache, '%s_%s_%s_align.apk' % (game, sdk, subChannel))

def getRenameApkPath(game, sdk, cache, name):
    '''
    重命名输出的apk名称
    '''
    return getFullPath('target', game, sdk, cache, '%s.apk' % name)

def getSignApkPath(game, sdk, subChannel, cache):
    '''
    获取输出的apk的目录
    '''
    return getFullPath('target', game, sdk, cache, '%s_%s_%s_signed.apk' % (game, sdk, subChannel))

def getPackagePath(basePath, packageName):
    '''
    包名对应的目录
    '''
    packageNameSplit = packageName.split('.')
    newPath = basePath
    for item in packageNameSplit:
        newPath = os.path.join(newPath, item)
    return newPath

def getToolWithSystem(tool):
    '''
    获取系统相关工具
    '''
    system = getSystemPath()
    suffix = getSystemSuffix()
    return os.path.join(getFullToolPath(system), tool + suffix)

def getSystemPath():
    '''
    获取系统目录
    '''
    if platform.system() == 'Windows':
        return 'win'
    elif platform.system() == 'Darwin':
        print ('---------macos----------')
        return 'macos'
    else:
        return 'linux'

def getSystemSuffix():
    '''
    系统工具后缀
    '''
    if platform.system() == 'Windows':
        return '.exe'
    else:
        return ''

def getApplicationSmaliIndex(file):
    '''
    获取application.smali的MultiDex信息
    '''
    content = ('invoke-super', '->attachBaseContext(Landroid/content/Context;)V')
    index = -1
    with openFile(file, 'r') as f:
        line = f.readline()
        while line:
            if content[0] in line and content[1] in line:
                index = f.tell()
                break
            line = f.readline()

    return index

def insertApplicationSmali(file, index):
    '''
    获取application.smali插入MultiDex的信息
    '''
    if index == -1:
        # append
        content = '''# virtual methods
.method protected attachBaseContext(Landroid/content/Context;)V
    .locals 0
    .param p1, "context"    # Landroid/content/Context;

    invoke-super {p0, p1}, Landroid/app/Application;->attachBaseContext(Landroid/content/Context;)V

    invoke-static {p0}, Landroid/support/multidex/MultiDex;->install(Landroid/content/Context;)V

    return-void
.end method'''
        with openFile(file, 'a') as f:
            f.write(content)
    else:
        # insert
        content = '\n\tinvoke-static {p0}, Landroid/support/multidex/MultiDex;->install(Landroid/content/Context;)V\n'
        with openFile(file, 'r+') as f:
            f.seek(index)
            last = f.read()
            f.seek(index)
            f.write(content)
            f.write(last)
    return 0

def changeVersion(yml, versionCode = None, versionName = None, targetSdkVersion = None):
    if versionCode is None and versionName is None and targetSdkVersion is None:
        return 0

    tag = ['versionCode:', 'versionName:', 'targetSdkVersion:']
    
    with openFile(yml, 'r+') as f:
        content = ''
        line = f.readline()
        while line:
            if versionCode is not None and tag[0] in line:
                content += '  versionCode: \'%s\'\n' % versionCode
            elif versionName is not None and tag[1] in line:
                content += '  versionName: %s\n' % versionName
            elif targetSdkVersion is not None and tag[2] in line:
                content += '  targetSdkVersion: \'%s\'\n' % targetSdkVersion
            else:
                content += line
            line = f.readline()

        f.seek(0, 0)
        f.truncate()
        f.write(content)

    return 0

def changeMinSdkVersion(yml, minSdkVersion):
    
    with openFile(yml, 'r+') as f:
        content = ''
        line = f.readline()
        while line:
            if 'minSdkVersion' in line:
                start = line.index("'")
                version = int(line[start+1:-2])
                if version < minSdkVersion:
                    content += '  minSdkVersion: \'%s\'\n' % minSdkVersion
                else:
                    return 0
            else:
                content += line
            line = f.readline()

        f.seek(0, 0)
        f.truncate()
        f.write(content)

    return 0

def list_files(src, resFiles):
    '''
    列出目录下所有的文件
    '''
    if os.path.exists(src):
        if os.path.isfile(src):
            resFiles.append(src)
        elif os.path.isdir(src):
            for f in os.listdir(src):
                list_files(os.path.join(src, f), resFiles)
    return resFiles

def getFileMd5(f):
    '''
    获取文件的md5
    '''
    m = hashlib.md5()
    while True:
        data = f.read(1024)  #将文件分块读取
        if not data:
            break
        m.update(data)
    return m.hexdigest()

def equalsFile(file1, file2):
    '''
    比较两个文件是否是同一个文件
    '''
    with open(file1, 'rb') as f1, open(file2, 'rb') as f2:
        file1Md5 = getFileMd5(f1)
        file2Md5 = getFileMd5(f2)
        #print('file1Md5:',file1Md5)
        #print('file2Md5:',file2Md5)
        return file1Md5 == file2Md5

def getExecPermission(file):
    '''
    linux下获取执行权限
    '''
    if platform.system() == 'Windows':
        return 0

    return execFormatCmd('chmod +x "%s"' % file)