package aQute.bnd.metatype;

import java.util.Arrays;
import java.util.Map;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.metatype.annotations.Designate;

import aQute.bnd.annotation.xml.XMLAttribute;
import aQute.bnd.osgi.Analyzer;
import aQute.bnd.osgi.Annotation;
import aQute.bnd.osgi.ClassDataCollector;
import aQute.bnd.osgi.Clazz;
import aQute.bnd.osgi.Descriptors.TypeRef;
import aQute.bnd.xmlattribute.XMLAttributeFinder;

public class DesignateReader extends ClassDataCollector {

	private Analyzer					analyzer;
	private Clazz						clazz;
	private Map<TypeRef,OCDDef>			classToOCDMap;

	private String[]					pids;
	private String						pid;
	private Annotation					designate;
	private final XMLAttributeFinder	finder;
	private DesignateDef				def;

	DesignateReader(Analyzer analyzer, Clazz clazz, Map<TypeRef,OCDDef> classToOCDMap, XMLAttributeFinder finder) {
		this.analyzer = analyzer;
		this.clazz = clazz;
		this.classToOCDMap = classToOCDMap;
		this.finder = finder;
	}

	static DesignateDef getDesignate(Clazz c, Analyzer analyzer, Map<TypeRef,OCDDef> classToOCDMap,
			XMLAttributeFinder finder) throws Exception {
		DesignateReader r = new DesignateReader(analyzer, c, classToOCDMap, finder);
		return r.getDef();
	}

	private DesignateDef getDef() throws Exception {
		clazz.parseClassFileWithCollector(this);
		if (pid != null && designate != null) {
			if (pids != null && pids.length > 1) {
				analyzer.error(
						"DS Component %s specifies multiple pids %s, and a Designate which requires exactly one pid",
						clazz.getClassName().getFQN(), Arrays.asList(pids));
				return null;
			}
			TypeRef ocdClass = designate.get("ocd");
			// ocdClass = ocdClass.substring(1, ocdClass.length() - 1);
			OCDDef ocd = classToOCDMap.get(ocdClass);
			if (ocd == null) {
				analyzer.error("DS Component %s specifies ocd class %s which cannot be found; known classes %s",
						clazz.getClassName().getFQN(), ocdClass, classToOCDMap.keySet());
				return null;
			}
			String id = ocd.id;
			boolean factoryPid = Boolean.TRUE == designate.get("factory");
			if (def == null)
				def = new DesignateDef(finder);
			def.ocdRef = id;
			def.pid = pid;
			def.factory = factoryPid;
			ocd.designates.add(def);
			return def;
		}
		return null;
	}

	@Override
	public void annotation(Annotation annotation) throws Exception {
		try {
			java.lang.annotation.Annotation a = annotation.getAnnotation();
			if (a instanceof Designate)
				designate = annotation;
			else if (a instanceof Component) {
				doComponent(a);
			} else {
				XMLAttribute xmlAttr = finder.getXMLAttribute(annotation);
				if (xmlAttr != null) {
					doXmlAttribute(annotation, xmlAttr);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			analyzer.error("During generation of a component on class %s, exception %s", clazz, e);
		}
	}

	void doComponent(java.lang.annotation.Annotation a) {
		Component component = (Component) a;
		pids = component.configurationPid();
		if (pids != null) {
			pid = pids[0];
		}
		if (pids == null || "$".equals(pid)) {
			pid = component.name();
			if (pid == null)
				pid = clazz.getClassName().getFQN();
		}
	}

	private void doXmlAttribute(Annotation annotation, XMLAttribute xmlAttr) {
		if (def == null)
			def = new DesignateDef(finder);
		def.addExtensionAttribute(xmlAttr, annotation);
	}

}
