//------------------------------------------------------------------------------
// Copyright (c) 2005, 2006 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 implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.library.edit.itemsfilter;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.epf.library.edit.PresentationContext;
import org.eclipse.epf.library.edit.util.MethodElementUtil;
import org.eclipse.epf.library.edit.util.ModelStructure;
import org.eclipse.epf.uma.Artifact;
import org.eclipse.epf.uma.ContentElement;
import org.eclipse.epf.uma.ContentPackage;
import org.eclipse.epf.uma.CustomCategory;
import org.eclipse.epf.uma.DescribableElement;
import org.eclipse.epf.uma.Discipline;
import org.eclipse.epf.uma.DisciplineGrouping;
import org.eclipse.epf.uma.Domain;
import org.eclipse.epf.uma.MethodElement;
import org.eclipse.epf.uma.MethodPlugin;
import org.eclipse.epf.uma.Process;
import org.eclipse.epf.uma.ProcessComponent;

/**
 * A helper class for managing element filtering in the method element selection
 * dialogs.
 * 
 * @author Shashidhar Kannoori
 * @since 1.0
 */
public class FilterHelper {

	private String pattern;

	private String type;

	private Pattern regex;

	private String tabStr;

	private Object contentElement;

	private List existingElements = new ArrayList();

	private boolean showPresentationName = false;

	public FilterHelper(Object contentElement, String tabStr, String pattern,

	String filterTypeStr, List alreadySelectedList) {

		this.contentElement = contentElement;

		this.tabStr = tabStr;

		this.type = filterTypeStr;

		this.existingElements = alreadySelectedList;

		this.pattern = pattern;

		showPresentationName = PresentationContext.INSTANCE
		.isShowPresentationNames();


	}

	public List getAlreadySelectedList() {

		return existingElements;

	}

	public void setAlreadySelectedList(List alreadySelectedList) {

		this.existingElements = alreadySelectedList;

	}

	public boolean matchPattern(Object obj) {
		return match(obj);
	}

	/*
	 * Convenient method to check any CategorizedElements (tree traversal) of
	 * CustomCategory has any match with matcher. @param CustomCategory
	 */
	private boolean hasMatchedChildCustomCategory(CustomCategory topCategory) {
		List list = topCategory.getCategorizedElements();

		for (Iterator iter = list.iterator(); iter.hasNext();) {
			Object object = iter.next();
			if (object instanceof CustomCategory) {
				CustomCategory category = (CustomCategory) object;
				if (matcher(category)) {
					return true;
				} else if (hasMatchedChildCustomCategory(category)) {
					return true;
				}
			}
		}
		return false;
	}

	/*
	 * Convenient method to check any subArtifacts (tree traversal) of Artifact
	 * has any match with matcher. @param CustomCategory
	 */
	private boolean hasMatchedArtifact(Artifact artifact) {

		List list = artifact.getContainedArtifacts();
		for (Iterator iter = list.iterator(); iter.hasNext();) {
			Object object = iter.next();
			if (object instanceof Artifact) {
				Artifact subartifact = (Artifact) object;
				if (matcher(subartifact)) {
					return true;
				} else if (hasMatchedArtifact(subartifact)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Convenient method to check any ChildPackages (tree traversal) of Content
	 * Package has any match with matcher.
	 * 
	 * @param CustomCategory
	 */
	private boolean hasMatchedChild(ContentPackage pkg) {

		for (Iterator iter = pkg.getChildPackages().iterator(); iter.hasNext();) {
			Object childPkg = iter.next();
			if (childPkg instanceof ContentPackage) {
				ContentPackage contentPkg = (ContentPackage) childPkg;
				if (matcher(contentPkg)) {
					return true;
				} else if (hasMatchedChild(contentPkg)) {
					return true;
				}
			}
		}
		return false;

	}

	/**
	 * Convenient method to check any subDomain (tree traversal) of Domain has
	 * any match with matcher <link>matcher(Object obj)</link>
	 * 
	 * @param CustomCategory
	 */
	private boolean hasMatchedSubDomains(Domain domain) {
		List list = domain.getSubdomains();
		for (Iterator iterator = list.iterator(); iterator.hasNext();) {
			Object subdomain = iterator.next();
			if (matcher(subdomain)) {
				return true;
			} else if (hasMatchedSubDomains((Domain) subdomain)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 
	 */
	private boolean hasMatchedSubDiscipline(Discipline discipline){
		List list = discipline.getSubdiscipline();
		for (Iterator iterator = list.iterator(); iterator.hasNext();) {
			Object subdiscipline = iterator.next();
			if (matcher(subdiscipline)) {
				return true;
			} else if (hasMatchedSubDiscipline((Discipline) subdiscipline)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 
	 */
	private boolean hasMatchedDisciplines(DisciplineGrouping grouping){
		List list = grouping.getDisciplines();
		for (Iterator iterator = list.iterator(); iterator.hasNext();) {
			Object subdiscipline = iterator.next();
			if (matcher(subdiscipline)) {
				return true;
			} else if (hasMatchedSubDiscipline((Discipline) subdiscipline)) {
				return true;
			}
		}
		return false;
	}

	public String getFilterTypeStr() {

		// Trim "-"(hyphen) from the strings (types).  Only in case methodplugin
		// don't trim "-" because methodplug-in contain "-" (hyphen)and useful.
		while (type != null && type.indexOf(FilterConstants.space) > -1
				&& !type.equals(FilterConstants.METHO_PLUGINS)) {
			type = type.substring(type.indexOf(FilterConstants.space) + 1, type
					.length());
		}
		return type;
	}

	public String getPattern() {

		return pattern;

	}

	public Pattern getRegexPattern() {

		return regex;

	}

	public String getTabStr() {

		return tabStr;

	}

	/*
	 * 
	 * Method checks whether arbitrary object passed is
	 * 
	 * equal to current content element's variability or not.
	 * 
	 * If variability of ContentElement matches arbitrary object,
	 * 
	 * returns true.
	 * 
	 * @retun boolean.
	 * 
	 */

	public boolean checkVariability(Object obj) {
		if (contentElement instanceof ContentElement) {

			Object variabilityObject = ((ContentElement) contentElement)
					.getVariabilityBasedOnElement();
			if (variabilityObject != null) {
				if (obj.equals(variabilityObject))
					return true;
			}
		}
		return false;
	}

	public Object getContentElement() {

		return contentElement;

	}

	public void setContentElement(Object contentElement) {

		this.contentElement = contentElement;

	}

	public void setFilterTypeStr(String filterTypeStr) {

		this.type = filterTypeStr;

	}

	public void setPattern(String pattern) {

		this.pattern = pattern;

	}

	public void setRegexPattern(Pattern regexPattern) {

		this.regex = regexPattern;

	}

	public void setTabStr(String tabStr) {

		this.tabStr = tabStr;

	}

	public String[] getPathBasedOnTabString(String tabStr) {

		if (FilterConstants.contentElementStrs.contains(tabStr)) {
			return ModelStructure.DEFAULT.coreContentPath;
		} else if (tabStr.equals(FilterConstants.ROLESETS)
				|| tabStr.equals(FilterConstants.ROLE_SET_GROUPINGS)) {
			return ModelStructure.DEFAULT.roleSetPath;
		} else if (tabStr.equals(FilterConstants.DISCIPLINES)
				|| tabStr.equals(FilterConstants.DISCIPLINE_GROUPINGS)) {
			return ModelStructure.DEFAULT.disciplineDefinitionPath;
		} else if (tabStr.equals(FilterConstants.TOOLS)) {
			return ModelStructure.DEFAULT.toolPath;
		} else if (tabStr.equals(FilterConstants.WORKPRODUCTTYPES)) {
			return ModelStructure.DEFAULT.workProductTypePath;
		} else if (tabStr.equals(FilterConstants.DOMAINS)) {
			return ModelStructure.DEFAULT.domainPath;
		} else if (tabStr.equals(FilterConstants.CUSTOM_CATEGORIES)) {
			return ModelStructure.DEFAULT.customCategoryPath;
		} else
			return ModelStructure.DEFAULT.coreContentPath;

	}

	/**
	 * 
	 * This is convenient method to check all the condition, like matchPattern,
	 * if object exists in already selected list, check variability match, and
	 * is object itself donot display.
	 * 
	 * @param object
	 * @return boolean
	 */

	public boolean checkObjectAccepted(Object obj) {

		if (!match(obj))
			return false;
		// Check if already existing (associated elements) don't display.
		if (getAlreadySelectedList() != null
				&& getAlreadySelectedList().contains(obj))
			return false;

		if (checkVariability(obj))
			return false;

		// if object itself don't display
		// Do equal check only on elements otherthan method plugin, In case of
		// Copyright(supporting material) associating to methodplugin, if
		// methodplugin (contentElemtn) equals
		// obj, then results false.Plugin will lost from tree in the filter. In
		// turn supporting material for that particular plugin will not be
		// displayed.
		if (!(obj instanceof MethodPlugin)) {
			if (contentElement != null && obj.equals(contentElement))
				return false;
		}
		return true;

	}

	/**
	 * 
	 * Convenient method for checking MethodPlugin accepted or not. Method needs
	 * to invoked only for MethodPlugin object.
	 * 
	 * @param object
	 * @return boolean
	 */

	public boolean acceptMethodPlugin(Object obj) {
		if (contentElement != null) {
			if (MethodElementUtil.getAllModels(contentElement).contains(obj))
				return true;
			else
				return false;
		} else {
			return true;
		}
	}

	/**
	 * Convenient method for checking Content Package accepted or not. Method
	 * needs to invoked only for Content Package object.
	 * 
	 * @param object
	 * @return boolean
	 */
	public boolean acceptContentPackage(Object obj) {

		if (((ContentPackage) obj).getChildPackages().isEmpty()
				&& ((ContentPackage) obj).getContentElements().isEmpty())
			return false;
		else
			return true;
	}

	public boolean isObjectInSelectedItems(Object obj) {

		if (getAlreadySelectedList() != null
				&& getAlreadySelectedList().contains(obj))
			return false;
		else
			return true;
	}

	public boolean isContributor(ContentElement element) {
		// Just returning true, because contribution checked in other place.
		return false;
	}

	public boolean matchPatternBasedOnType(Object obj) {
		return match(obj);
	}

	public boolean matchPatternOnPresentationName(Object obj) {
		return match(obj);
	}

	public boolean isShowPresentationName() {
		return showPresentationName;
	}

	public void setShowPresentationName(boolean showPresentationName) {
		this.showPresentationName = showPresentationName;
	}

	/**
	 * Generic method to check match of an object based on regular expression
	 * also based on type that selected in the dialog.
	 */

	private boolean match(Object obj) {

		// Do not do Pattern match if type or pattern string is null.
		if (type == null || pattern == null || pattern.equalsIgnoreCase("")) //$NON-NLS-1$
			return true;

		// Check if type is of Content Package
		if (type.equals(FilterConstants.CONTENT_PACKAGES)) {

			if (obj instanceof ContentPackage) {

				if (matcher(obj) || hasMatchedChild((ContentPackage) obj)) {
					return true;
				} else {
					return false;
				}

			} else {

				// else for all content elements, if their content pakcage
				// matches , return true.
				if (obj instanceof ContentElement) {
					Object container = ((ContentElement) obj).getContainer();
					if (container instanceof ContentPackage) {
						if (!matcher(container))
							return false;
					}
				}
				return true;
			}

		} else if (type.equals(FilterConstants.METHO_PLUGINS)) {
			// Check if type is of Method Plugins
			if (obj instanceof MethodPlugin) {
				// if is a MethodPlugin, return true if matches the pattern
				return matcher(obj);

			} else {
				return true;
			}

		} else if (type != null) {

			if (obj instanceof ContentElement
					|| obj instanceof ProcessComponent
					|| obj instanceof Process) {

				if (type.indexOf("-") > -1) {
					type = type.substring(type.indexOf("-"), type.length());
				}

				Class cls = FilterInitializer.getInstance().getClassForType(
						type);
				if (cls != null && !cls.isInstance(obj)) {
					return true;
				}

				if (matcher(obj)) {
					return true;
				} else {

					if (obj instanceof CustomCategory)
						return hasMatchedChildCustomCategory((CustomCategory) obj);

					if (obj instanceof Artifact) {
						return hasMatchedArtifact((Artifact) obj);
					}
					if (obj instanceof Domain) {
						return hasMatchedSubDomains((Domain) obj);
					}
					if (obj instanceof Discipline){
						return hasMatchedSubDiscipline((Discipline)obj);
					}
					if (obj instanceof DisciplineGrouping){
						return hasMatchedDisciplines((DisciplineGrouping)obj);
					}
					return false;
				}
			} else { // else for all other types of elements
				return true;
			}

		} else
			// the All case
			return true;
	}

	/**
	 * Convenient method which does actual matching of given object with regular
	 * expression. Checks to do presentationname or name.
	 */
	public boolean matcher(Object obj) {
		Matcher m = regex.matcher(((MethodElement) obj).getName());
		if (showPresentationName) {
			if (obj instanceof DescribableElement
					&& ((DescribableElement) obj).getPresentationName() != null)
				m = regex.matcher(((DescribableElement) obj)
						.getPresentationName());
		}
		return m.matches();
	}
}
