package net.csdn.filter;

import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import net.csdn.ServiceFramwork;
import net.csdn.common.logging.CSLogger;
import net.csdn.common.logging.Loggers;
import net.csdn.common.settings.Settings;
import net.csdn.enhancer.ControllerEnhancer;

import java.io.DataInputStream;
import java.lang.reflect.Modifier;
import java.util.List;

import static net.csdn.common.collections.WowCollections.list;

/**
 * 4/9/13 WilliamZhu(allwefantasy@gmail.com)
 */
public class FilterEnhancer extends ControllerEnhancer {

    private Settings settings;
    private CSLogger logger = Loggers.getLogger(FilterEnhancer.class);


    public FilterEnhancer(Settings settings) {
        this.settings = settings;
    }


    private List<String> shouldNotCopyToSubclassStaticMethods = list(

    );

    @Override
    public CtClass enhanceThisClass(DataInputStream dataInputStream) throws Exception {
        CtClass ctClass = classPool.makeClassIfNew(dataInputStream);
        if (!ctClass.subtypeOf(classPool.get("net.csdn.modules.http.ApplicationController"))) {
            return null;
        }

        if (Modifier.isAbstract(ctClass.getModifiers())) return null;


        CtClass controller = classPool.get("net.csdn.modules.http.ApplicationController");

        //copy static fields to subclass.Importance because of inheritance strategy of java
        copyStaticFieldsToSubclass(controller, ctClass);

        //copy static methods to subclass
        copyStaticMethodsToSubclass(controller, ctClass);

        return ctClass;
    }

    private void copyStaticFieldsToSubclass(CtClass document, CtClass targetClass) throws Exception {
        CtField[] ctFields = document.getFields();
        for (CtField ctField : ctFields) {
            if (Modifier.isStatic(ctField.getModifiers()) && ctField.getName().startsWith("parent$_")) {
                CtField ctField1 = new CtField(ctField.getType(), ctField.getName(), targetClass);
                ctField1.setModifiers(ctField.getModifiers());
                targetClass.addField(ctField1);
            }
        }

    }

    private void copyStaticMethodsToSubclass(CtClass document, CtClass targetClass) throws Exception {
        CtMethod[] ctMethods = document.getMethods();

        for (CtMethod ctMethod : ctMethods) {
            if (Modifier.isStatic(ctMethod.getModifiers()) && !shouldNotCopyToSubclassStaticMethods.contains(ctMethod.getName())) {
                CtMethod ctNewMethod = CtNewMethod.copy(ctMethod, targetClass, null);
                targetClass.addMethod(ctNewMethod);
            }

        }
    }

    @Override
    public void enhanceThisClass2(List<CtClass> ctClasses) throws Exception {
        for (CtClass ctClass : ctClasses) {
            if (Modifier.isAbstract(ctClass.getModifiers())) continue;
            try {
                ctClass.toClass(ServiceFramwork.scanService.getLoader().getClassLoader(), ServiceFramwork.scanService.getLoader().getProtectionDomain());
            } catch (Exception e) {
                logger.error("Fail to load ctClass " + ctClass.getName(), e);
            }

        }
    }
}
