package com.topsunit.query.binding;

import com.huigou.data.domain.query.QueryAbstractRequest;
import com.topsunit.query.annotations.Param;
import javassist.*;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Collection;

/**
 * 利用Javassist生成QueryRequest对象。
 *
 * @author yonghuan
 */
public class JavassistQueryRequestFactory extends AbstractQueryRequestFactory implements QueryRequestFactory {

    @Override
    protected QueryAbstractRequest newInstance(Method method, Object[] args) {
        try {
            Class<?> clazz = getClass(method);
            return (QueryAbstractRequest) clazz.newInstance();
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    private Class<?> getClass(Method method) throws NotFoundException, CannotCompileException {
        ClassPool classPool = new ClassPool(true);
        classPool.insertClassPath(new LoaderClassPath(JavassistQueryRequestFactory.class.getClassLoader()));
        String className = method.getDeclaringClass().getPackage().getName() + "." + method.getName() + "QueryRequest";
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
        }
        CtClass cc = classPool.makeClass(className);
        cc.setSuperclass(classPool.get("com.huigou.data.domain.query.QueryAbstractRequest"));
        Parameter[] parameters = method.getParameters();
        for (Parameter parameter : parameters) {
            Param paramAnnotation = parameter.getAnnotation(Param.class);
            if (paramAnnotation != null) {
                Class parameterType = parameter.getType();
                if (Collection.class.isAssignableFrom(parameterType)) {
                    parameterType = String.class;
                }
                String propertyName = paramAnnotation.value();
                CtField cf = CtField.make(String.format("private %s %s;", parameterType.getName(), propertyName), cc);
                cc.addField(cf);
                String upperCamelCaseName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
                CtMethod setter = CtMethod.make(String.format("public void set%s(%s %s){ this.%s = %s; }", upperCamelCaseName, parameterType.getName(), propertyName, propertyName, propertyName), cc);
                cc.addMethod(setter);
                CtMethod getter = CtMethod.make(String.format("public %s get%s(){ return this.%s; }", parameterType.getName(), upperCamelCaseName, propertyName), cc);
                cc.addMethod(getter);
            }
        }
        return cc.toClass();
    }
}
