/**
 * License Agreement.
 *
 * Rich Faces - Natural Ajax for Java Server Faces (JSF)
 *
 * Copyright (C) 2007 Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

package org.ajax4jsf.builder.generator;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import net.sf.cglib.core.ClassEmitter;
import net.sf.cglib.core.CodeEmitter;
import net.sf.cglib.core.EmitUtils;
import net.sf.cglib.core.MethodInfo;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Constants;
import org.objectweb.asm.Type;

/**
 * @author Nick - mailto:nbelaevski@exadel.com
 * created 20.04.2007
 * 
 */
public class AbstractClassStubBuilder {
	public static <T> T buildStub(final Class<T> clazz, MethodInterceptor interceptor, 
			ClassLoader classLoader) {
		Class<T> instrumentedClass;
		
		if ((Modifier.ABSTRACT & clazz.getModifiers()) != 0) {
			Enhancer enhancer = new Enhancer() {
				@Override
				public void generateClass(ClassVisitor v) throws Exception {
			        ClassEmitter ce = new ClassEmitter(v);
			        ce.begin_class(Constants.V1_5,
			                       Constants.ACC_PUBLIC,
			                       getClassName(),
			                       Type.getType(clazz),
			                       null,
			                       null);
			        EmitUtils.null_constructor(ce);
			        List methods = new ArrayList();
			        getMethods(clazz, null, methods);
			        
			        for (Iterator iterator = methods.iterator(); iterator
							.hasNext();) {
						Method method = (Method) iterator.next();
						
						if (Modifier.isAbstract(method.getModifiers())) {
							MethodInfo methodInfo = ReflectUtils.getMethodInfo(method);
							Signature signature = methodInfo.getSignature();
							Type returnType = signature.getReturnType();
							
							CodeEmitter e = ce.begin_method(method.getModifiers() & ~Modifier.ABSTRACT, 
									signature, methodInfo.getExceptionTypes(), 
									methodInfo.getAttribute());
							
							e.zero_or_null(returnType);
							e.return_value();

							Type[] argumentTypes = methodInfo.getSignature().getArgumentTypes();
							int size = 0;
							if (argumentTypes != null) {
								for (int i = 0; i < argumentTypes.length; i++) {
									size += argumentTypes[i].getSize();
								}
							}
							
							// 1 is for this
							e.visitMaxs(returnType.getSize(), size + 1);
						
							e.end_method();
						}
			        }
			        
			        ce.end_class();
				}
			};
			enhancer.setSuperclass(clazz);
			enhancer.setCallbackType(MethodInterceptor.class);
			enhancer.setClassLoader(classLoader != null ? 
					classLoader : clazz.getClassLoader());
			instrumentedClass = enhancer.createClass(); 
		} else {
			instrumentedClass = clazz;
		}

		Enhancer enhancer2 = new Enhancer();
		enhancer2.setSuperclass(instrumentedClass);
		enhancer2.setCallback(interceptor);
		enhancer2.setClassLoader(instrumentedClass.getClassLoader());

		return (T) enhancer2.create();
	}
}

