/**
 * 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) {
		Enhancer enhancer = new Enhancer() {
			@Override
			public void generateClass(ClassVisitor v) throws Exception {
		        ClassEmitter ce = new ClassEmitter(v);
		        ce.begin_class(Constants.V1_4,
		                       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());
		Class<T> instrumentedClass = enhancer.createClass(); 

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

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

