package com.gradle.ares

import com.android.build.api.transform.TransformException
import com.android.build.api.transform.TransformInvocation
import com.gradle.JarZipUtil
import com.gradle.ModifyClassTransform
import com.gradle.MyLogger
import com.intellij.openapi.util.io.FileUtil
import org.gradle.api.Project
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter

/**
 * Created by Glen on 2017/2/7.
 */

public class AresTransform extends ModifyClassTransform {
    public AresTransform(Project project) {
        super(project);
        if (project != null) {
            project.extensions.create('ares', AresParams)
            project.ares.extensions.create('to', AresParams.To)
            MyLogger.lifecycle("set:" + AresParams.toString())
        }
    }

    @Override
    String getName() {
        return "AresPlugin"
    }

    @Override
    void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
        initParams()
        super.transform(transformInvocation)
    }

    @Override
    public void handleChangedFile(File input) {
        String fileParentDir = input.parentFile.absolutePath.replace("/", "\\")
        String fileAbsName = input.absolutePath.replace("/", "\\")

        if (input.isFile()) {
            MyLogger.info("scan:" + fileAbsName)
            if (fileAbsName.endsWith(".class")) {
                modifyClass(input)
            } else if (fileAbsName.endsWith(".jar")) {
                for (String src : AresValue.Excludes) {
                    if (fileAbsName.contains(src)) {
                        return
                    }
                }
                //需要处理的jar包
                File tmp = new File(input.getParent() + File.separator + input.name.substring(0, input.name.length() - 4))
                tmp.deleteDir()

                JarZipUtil.unzipJar(input, tmp.absolutePath)

                boolean modify = false
                tmp.eachFileRecurse { File file ->
                    if (file.isFile() && file.name.endsWith(".class")) {
                        if (modifyClass(file)) {
                            modify = true
                        }
                    }
                }
                if (modify) {
                    MyLogger.lifecycle("jar:modify:" + fileAbsName)
                    JarZipUtil.zipJar(tmp.absolutePath, input)
                }
            }
        }
    }

    /**
     * 初始化参数
     */
    private void initParams() {
        def ares = project.ares
        def to = project.ares.to
        if (ares.agentClass != null) {
            AresValue.AgentClass = ares.agentClass
        } else {

        }
        if (ares.lifecycleClass != null) {
            AresValue.LifeClass = ares.lifecycleClass
        }
        if (ares.agentList != null) {
            for (String item : ares.agentList) {
                if (item.contains("(") && item.contains(")")) {
                    String name = item.substring(0, item.indexOf("("))
                    String desc = item.substring(item.indexOf("("), item.length())
                    MethodAgentVisitor.Modify.add(new MethodModel(name, desc))
                }
            }
        }
        if (ares.excludes != null) {
            AresValue.Excludes = ares.excludes
        }

        MyLogger.lifecycle("init:agent:" + AresValue.AgentClass)
        MyLogger.lifecycle("init:lifecycle:" + AresValue.LifeClass)
        MyLogger.lifecycle("init:excludes:" + AresValue.Excludes)
        MyLogger.lifecycle("init:to:other:" + (to == null ? null : to.other))
    }

    /**
     * 修改类文件
     * @param clazz
     * @return
     */
    private boolean modifyClass(File clazz) {
        ClassReader cr = new ClassReader(clazz.bytes)
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS)
        ClassAdapter adapter = new ClassAdapter(cw, project)
        cr.accept(adapter, ClassReader.EXPAND_FRAMES)

        if (adapter.modify) {
            FileOutputStream out = new FileOutputStream(clazz)
            out.write(cw.toByteArray())
            out.close()
        }
        return adapter.modify
    }
}