package jp.sourceforge.asclipse.as3.internal;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jp.sourceforge.asclipse.as3.IAS3Context;
import jp.sourceforge.asclipse.as3.IAS3GlobalContext;
import jp.sourceforge.asclipse.as3.IAS3Context.IAS3ContextListener;
import jp.sourceforge.asclipse.as3.element.AS3Root;
import jp.sourceforge.asclipse.as3.element.AS3Type;
import jp.sourceforge.asclipse.as3.internal.resolver.TypeResolver;
import jp.sourceforge.asclipse.as3.resolver.AS3TypeRef;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * {@link IAS3GlobalContext}の内部用の実装。
 * @author shin1ogawa
 */
public class DefaultAS3GlobalContext implements IAS3GlobalContext {

	static final Logger LOGGER = LoggerFactory.getLogger(IAS3GlobalContext.class);

	final List<IAS3Context> contexts = new ArrayList<IAS3Context>();

	final Map<IAS3Context, IAS3ContextListener> listenerMap =
			new HashMap<IAS3Context, IAS3ContextListener>();

	private final TypeResolver typeResolver;


	/**
	 * Constructor.
	 * @category constructor
	 */
	public DefaultAS3GlobalContext() {
		typeResolver = new TypeResolver(this);
	}

	public void addContext(IAS3Context context) {
		if (contexts.contains(context)) {
			return;
		}
		contexts.add(context);
		IAS3ContextListener listener = new IAS3ContextListener() {

			public void added(AS3Root root) {
				// TODO Auto-generated method stub
			}

			public void removed(AS3Root root) {
				// TODO Auto-generated method stub
			}
		};
		context.addListener(listener);
		listenerMap.put(context, listener);
	}

	public void removeContext(IAS3Context context) {
		if (!contexts.contains(context)) {
			return;
		}
		contexts.remove(context);
		context.removeListener(listenerMap.get(context));
		listenerMap.remove(context);
	}

	public IAS3Context getContext(String name) {
		for (IAS3Context context : contexts) {
			if (context.getRootFolder().equals(name)) {
				return context;
			}
		}
		return null;
	}

	public boolean resolve(AS3TypeRef typeRef, AS3Type enclosureType) {
		return typeResolver.resolve(typeRef, enclosureType);
	}

	public AS3Type getTypeElement(String qualifiedName) {
		for (IAS3Context context : contexts) {
			AS3Type typeElement = context.getTypeElement(qualifiedName);
			if (typeElement != null) {
				return typeElement;
			}
		}
		return null;
	}

	public List<AS3Type> getTypesInPackage(String packageName) {
		List<AS3Type> types = new ArrayList<AS3Type>();
		for (IAS3Context context : contexts) {
			types.addAll(context.getTypesInPackage(packageName));
		}
		return types;
	}

	public List<AS3Type> getAllTypes() {
		List<AS3Type> types = new ArrayList<AS3Type>();
		for (IAS3Context context : contexts) {
			types.addAll(context.getAllTypes());
		}
		return types;
	}

	public List<AS3Root> getAS3Roots() {
		List<AS3Root> types = new ArrayList<AS3Root>();
		for (IAS3Context context : contexts) {
			types.addAll(context.getAS3Roots());
		}
		return types;
	}

	public List<IAS3Context> getContexts() {
		return Collections.unmodifiableList(contexts);
	}

	/**
	 * @return the typeResolver
	 * @category accessor
	 */
	public TypeResolver getTypeResolver() {
		return typeResolver;
	}

	public IAS3Context loadBuiltin(String name) throws IOException, ClassNotFoundException {
		ObjectInputStream objectInputStream = null;
		try {
			objectInputStream =
					new ObjectInputStream(DefaultAS3GlobalContext.class.getResourceAsStream(name));
			long currentTimeMillis = System.currentTimeMillis();
			LOGGER.debug("load start: " + name);
			IAS3Context builtinContext = (IAS3Context) objectInputStream.readObject();
			LOGGER.debug("load end:" + name + " [ms]="
					+ (System.currentTimeMillis() - currentTimeMillis));
			addContext(builtinContext);
			return builtinContext;
		} finally {
			if (objectInputStream != null) {
				objectInputStream.close();
			}
		}
	}
}
