/*******************************************************************************
 * Copyright (c) 2001, 2004 IBM Corporation and others.
 * 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:
 *     IBM Corporation - initial API and implementation
 *     Jens Lukowski/Innoopract - initial renaming/restructuring
 *     Exadel, Inc.
 *     Red Hat, Inc.     
 *******************************************************************************/
package org.jboss.tools.common.text.ext.hyperlink.xpl;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.IConfigurationElement;
import org.jboss.tools.common.text.ext.hyperlink.HyperlinkBuilder;
import org.jboss.tools.common.text.ext.util.xpl.RegistryReader;

public class BaseHyperlinkBuilder extends RegistryReader{
	// extension point ID
	public static final String PL_HYPERLINK = "hyperlink"; //$NON-NLS-1$

//	public static final String PLUGIN_ID = ExtensionsPlugin.PLUGIN_ID;

	public static final String TAG_HYPERLINK = "hyperlink"; //$NON-NLS-1$
	public static final String TAG_CONTENT_TYPE_IDENTIFIER = "contenttypeidentifier"; //$NON-NLS-1$
	public static final String TAG_PARTITION_TYPE = "partitiontype"; //$NON-NLS-1$

	public static final String ATT_ID = "id"; //$NON-NLS-1$
	public static final String ATT_CLASS = "class"; //$NON-NLS-1$

	protected String targetContributionTag;

	private static HyperlinkBuilder fInstance;

	private List fHyperlinkDefs = null;
	private HyperlinkDefinition fCurrentHyperlinkDefinition = null;
	private String fCurrentContentType;

	/**
	 * returns singleton instance of HyperlinkBuilder
	 * 
	 * @return HyperlinkBuilder
	 */
	public synchronized static HyperlinkBuilder getInstance() {
		if (fInstance == null) {
			fInstance = new HyperlinkBuilder();
		}
		return fInstance;
	}

	/**
	 * Returns the name of the part ID attribute that is expected
	 * in the target extension.
	 * 
	 * @param element
	 * @return String
	 */
	protected String getId(IConfigurationElement element) {
		String value = element.getAttribute(ATT_ID);
		return value;
	}

	protected String getHyperlinkClass(IConfigurationElement element) {
		String value = element.getAttribute(ATT_CLASS);
		return value;
	}

	/**
	 * Processes element which should be a configuration element specifying an
	 * open on object.  Creates a new open on definition object and adds it to the
	 * list of open on definition objects
	 * 
	 * @param element hyperlink configuration element
	 */
	private void processHyperlinkTag(IConfigurationElement element) {
		String theId = getId(element);
		String theClass = getHyperlinkClass(element);

		if (theId != null && theClass != null) {
			// start building new HyperlinkDefinition
			fCurrentHyperlinkDefinition = new HyperlinkDefinition(theId, theClass, element);

			// create a new list of open on definitions if it hasnt been created yet
			if (fHyperlinkDefs == null) {
				fHyperlinkDefs = new ArrayList();
			}
			fHyperlinkDefs.add(fCurrentHyperlinkDefinition);
		}
		else {
			fCurrentHyperlinkDefinition = null;
		}
	}

	/**
	 * Processes element which should be a configuration element specifying a content
	 * type for the current open on tag.  Assumes that there is a valid current open
	 * on definition object.
	 * 
	 * @param element contenttypeidentifier configuration element
	 */
	private void processContentTypeTag(IConfigurationElement element) {
		// add to current HyperlinkDefinition
		String theId = getId(element);

		if (theId != null) {
			fCurrentContentType = theId;
			fCurrentHyperlinkDefinition.addContentTypeId(fCurrentContentType);
		}
		else {
			fCurrentContentType = null;
		}
	}

	/**
	 * Processes element which should be a configuration element specifying a partition
	 * type for the current open on/content type tag.  Assumes that there is a valid
	 * current open on/content type tag.
	 * 
	 * @param element partitiontype configuration element
	 */
	private void processPartitionTypeTag(IConfigurationElement element) {
		// add to current HyperlinkDefinition/contentType
		String theId = getId(element);

		if (theId != null) {
			fCurrentHyperlinkDefinition.addPartitionType(fCurrentContentType, theId);
		}
	}

	/* (non-Javadoc)
	 * @see com.ibm.sse.editor.internal.extension.RegistryReader#readElement(org.eclipse.core.runtime.IConfigurationElement)
	 */
	protected boolean readElement(IConfigurationElement element) {
		String tag = element.getName();

		if (tag.equals(targetContributionTag)) {
			processHyperlinkTag(element);

			// make sure processing of current open on tag resulted in a current open on definition
			// before continue reading the children
			if (fCurrentHyperlinkDefinition != null) {
				readElementChildren(element);
			}
			return true;
		}
		else if (tag.equals(TAG_CONTENT_TYPE_IDENTIFIER)) {
			processContentTypeTag(element);

			// make sure processing of current content type resulted in a valid content type
			// before reading the children
			if (fCurrentContentType != null) {
				readElementChildren(element);
			}
			return true;
		}
		else if (tag.equals(TAG_PARTITION_TYPE)) {
			processPartitionTypeTag(element);
			return true;
		}

		return false;
	}

	private void initCache() {
		if (fHyperlinkDefs == null) {
			readContributions(TAG_HYPERLINK, PL_HYPERLINK);
		}
	}



	protected void readContributions(String tag_hyperlink2, String pl_hyperlink2) {
		
	}

	/**
	 * Returns all the open on definition objects
	 * @return
	 */
	public HyperlinkDefinition[] getHyperlinkDefinitions() {
		initCache();
		return (fHyperlinkDefs == null ? new HyperlinkDefinition[0] : 
		(HyperlinkDefinition[])fHyperlinkDefs.toArray(new HyperlinkDefinition[fHyperlinkDefs.size()]));
	}

	/**
	 * Returns all the open on definition objects valid for contentType/partitionType
	 * @param contentType
	 * @param partitionType
	 * @return if either contentType or partitionType is null, null is returned 
	 */
	public HyperlinkDefinition[] getHyperlinkDefinitions(String contentType, String partitionType) {
		if (contentType == null || partitionType == null) {
			// should not be able to define an hyperlink without a content type
			// but if it were possible then would need to search all hyperlink definitions for
			// definitions with empty contentType list
			return null;
		}

		// entire list of hyperlink definition objects
		HyperlinkDefinition[] allDefs = getHyperlinkDefinitions();
		// current list of open on definitions valid for contentType/partitionType
		List defs = new ArrayList();
		// default definitions that should be added to end of list of open on definitions
		List lastDefs = new ArrayList();

		for (int i = 0; i < allDefs.length; ++i) {
			// for each one check if it contains contentType
			List partitions = (List) allDefs[i].getContentTypes().get(contentType);
			if (partitions != null) {
				// this hyperlink definition is valid for all partition types for this content type
				if (partitions.isEmpty()) {
					// this will be added to end of list because this is considered a default hyperlink
					lastDefs.add(allDefs[i]);
				}
				else {
					// examine the partition types of this hyperlink
					int j = 0; // current index in list of partitions
					boolean added = false; // hyperlink has been added to list
					while (j < partitions.size() && !added) {
						// this hyperlink definition applies to partitionType so add to list of valid hyperlinks
						if (partitionType.equals(partitions.get(j))) {
							defs.add(allDefs[i]);
							added = true;
						}
						else {
							// continue checking to see if this hyperlink definition is valid for current partitionType
							++j;
						}
					}
				}
			}
		}
		// append the default hyperlink definitions
		defs.addAll(lastDefs);

		// return the list
		return (HyperlinkDefinition[]) defs.toArray(new HyperlinkDefinition[defs.size()]);
	}
	
}
