/*******************************************************************************
* Copyright (c) 2006 IONA Technologies PLC
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
* 
* Contributors:
*     IONA Technologies PLC - initial API and implementation
*******************************************************************************/
package org.eclipse.stp.sc.common.annotations.ext;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.stp.common.logging.LoggingProxy;
import org.eclipse.stp.sc.common.annotations.ext.IAnnotationInitializer;

public class AnnotationSupportProxy {

    private static final String EXT_ATT_RESOLVER_ID = "resolverId";
    private static final String EXT_ELT_SC_ANN = "annotation";
    
    private static final String EXT_ELT_RESOLVER = "annotationResolver";
    private static final String EXT_ATT_RESOLVER_CLS = "resolverClass";
    
    
    private static Map<String, AnnotationDef> ANN_DECL = 
    	new HashMap<String, AnnotationDef>();
    
    /**
     * since jdt annotation are simple name, we also need to keep a list of 
     * simple name indexed annotations
     */
    private static Map<String, AnnotationDef> ANN_SIMPLE_DECL = 
    	new HashMap<String, AnnotationDef>();
    private static Map<String, IAnnotationResolver> ANN_RESOLVER = 
    	new HashMap<String, IAnnotationResolver>();
    private static List<Class <? extends Annotation> > ANN_LIST = 
    	new ArrayList<Class <? extends Annotation> >();

    private static final LoggingProxy LOG = LoggingProxy.getlogger(AnnotationSupportProxy.class);
    public static final String EXT_POINT_SC_ANNOTATION_SUPPORT = "org.eclipse.stp.sc.common.AnnotationSupport";

    static {
    	loadAnnotationResolver();
    	loadAnnotations();
    	loadAnnotationClass();
    }
    private AnnotationSupportProxy() {
    	
    }
    
    private static void loadAnnotationClass() {
        for (AnnotationDef def : ANN_DECL.values()) {
        	IAnnotationResolver resolver = ANN_RESOLVER.get(def.resolverId);
        	try {
				def.annotation = resolver.loadAnnotationClass(def.className);
				ANN_LIST.add(def.annotation);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
				LOG.error("error when try to load ann class:" + def.className, e);
			}
        }
    }
    
    /**
     * load all annotation resolvers defined in the system
     */
    private static void loadAnnotationResolver() {
    	IExtensionRegistry reg = Platform.getExtensionRegistry();
        IConfigurationElement[] extElts = reg.getConfigurationElementsFor(
        		EXT_POINT_SC_ANNOTATION_SUPPORT);
        
        for (IConfigurationElement elem : extElts) {
        	if (elem.getName().equals(EXT_ELT_RESOLVER)) {
        		try {
					IAnnotationResolver resolverClass = (IAnnotationResolver) elem
							.createExecutableExtension(EXT_ATT_RESOLVER_CLS);
					String resolverId = elem.getAttribute(EXT_ATT_RESOLVER_ID);
					LOG.debug("load annotation resolver:" + resolverId);
					ANN_RESOLVER.put(resolverId, resolverClass);
				} catch (CoreException ce) {
					LOG.error(ce);
				}
        	}
        }
        
    }
    
    
    public static Iterator <Class <? extends Annotation>> getAllAnnotationClasses() {
    	return ANN_LIST.iterator();
    }
    
    public static void loadAnnotations() {
        IExtensionRegistry reg = Platform.getExtensionRegistry();
        IConfigurationElement[] extElts = reg.getConfigurationElementsFor(
        		EXT_POINT_SC_ANNOTATION_SUPPORT);
        
        for (IConfigurationElement elem : extElts) {
            if (elem.getName().equals(EXT_ELT_SC_ANN)) {
            	AnnotationDef def = new AnnotationDef();
            	try {
					def.loadAnnotation(elem);
				} catch (CoreException e) {
					e.printStackTrace();
					LOG.error("error during load ann:");
				}
				String clsName = def.className;
            	ANN_DECL.put(clsName, def);
            	String simpleName = clsName.substring(clsName.lastIndexOf(".") + 1);
            	ANN_SIMPLE_DECL.put(simpleName, def);
            }
        }
    }
    

    
    public static boolean verifyNature(String annClassName, String[] natures) {
    	AnnotationDef def = ANN_DECL.get(annClassName);
    	String definedNature = def.natureId;
    	if (definedNature == null) {
    		return true;
    	}
    	for (String nature : natures) {
    		if (nature.equals(definedNature)) {
    			return true;
    		}
    	}
    	return false;
    }

    public static boolean isClassOnly(String annotationName) {
    	AnnotationDef def = ANN_DECL.get(annotationName);
    	return def.classOnly;
    }

    public static boolean isInterfaceOnly(String annotationName) {
    	AnnotationDef def = ANN_DECL.get(annotationName);
    	return def.interfaceOnly;
    }
    
    public static boolean hasAnnotation(String annotationName) {
    	boolean ret = ANN_DECL.containsKey(annotationName);
    	if (!ret) {
    		return ANN_SIMPLE_DECL.containsKey(annotationName);
    	} else {
    		return ret;
    	}
    }
    
    public static Class <? extends Annotation> getAnnotationClass(String annotationName) {
    	AnnotationDef def = ANN_DECL.get(annotationName);
    	if (def == null) {
    		def = ANN_SIMPLE_DECL.get(annotationName);
    	}
    	if (def != null) {
    	    return def.annotation;
    	}
    	return null;
    }
    



    public static Class <? extends Annotation> getAvailableAnnotation(Class annClass) {
    	AnnotationDef def = ANN_DECL.get(annClass.getName());
    	if (def == null) {
    		return null;
    	}
    	return def.annotation;
    }

     

    public static IAnnotationInitializer getAvailableAnnotationInitializer(Class annClass) {
    	AnnotationDef def = ANN_DECL.get(annClass.getName());
    	return def.initializerClass;
    }

    public static List<MemberValuePair> getDefaultAttributes(Class<? extends Annotation> annotationClass,
                                                             CompilationUnit astRoot,
                                                             IMember jdtMember,
                                                             SingleVariableDeclaration jdtMemberParam) {
        
        IAnnotationInitializer init = getAvailableAnnotationInitializer(annotationClass);
        if (init != null) {
            return init.getDefaultAttributes(annotationClass, astRoot, jdtMember, jdtMemberParam);
        }
        return null;
    }


}
