//------------------------------------------------------------------------------
// 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.publishing.services;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.epf.library.util.LibraryUtil;
import org.eclipse.epf.publishing.PublishingPlugin;
import org.eclipse.epf.uma.BreakdownElement;
import org.eclipse.epf.uma.ContentCategory;
import org.eclipse.epf.uma.Descriptor;
import org.eclipse.epf.uma.Guidance;
import org.eclipse.epf.uma.MethodElement;
import org.eclipse.epf.uma.Practice;
import org.eclipse.epf.uma.Roadmap;
import org.eclipse.epf.uma.SupportingMaterial;
import org.eclipse.epf.uma.TermDefinition;


public class ProcessPublishingContentValidator extends PublishingContentValidator {

	public static final String CLOSURE_LOG_FILENAME = "processClosure.html"; //$NON-NLS-1$

	// closure elements for process publishing, null for config publishing
	List closureElements = null;
	private boolean isFinalClosure = false;
	
//	// maintain the object references generated from TaskDescriptors
//	// map of owner element to a list of referenced elements
//	Map objReferences = new HashMap();
	
	private boolean debug = PublishingPlugin.getDefault().isDebugging();
	
	protected PrintStream closureLog = null;
	
	public ProcessPublishingContentValidator(String pubDir, boolean validateExternalLinks) {
		super(pubDir, validateExternalLinks);
		
		closureLog = getStream(CLOSURE_LOG_FILENAME);
		beginClosureLog();

	}

	public void addClosureElements(List items) {
		// do nothing
		if ( closureElements == null ) {
			closureElements = new ArrayList();
		}
		
		closureElements.addAll(items);
	}

	/**
	 * 	make element closure 
	 *  all the published and referenced elements are the element closure. 
	 *  since they are all the elements referenced by the processes and their related process elements
	 * 
	 * @param isFinalClosure boolean if true the closure is final, 
	 * anything outside the closure is treated as not in closure. 
	 * if false, the closure is final, any Guidances referenced by the elements in closure is treated as in closure.
	 * we need this to allow build the closure in steps but still not create broken links for published elements.
	 */
	public void makeElementClosure() {
		this.isFinalClosure = true;
		
		endClosureLog();
		
		// test
		if ( debug) {
			System.out.println("====================== Closure elements ======================="); //$NON-NLS-1$
			for (Iterator it = closureElements.iterator(); it.hasNext(); ) {
				System.out.println(LibraryUtil.getTypeName((MethodElement)it.next()));
			}
			System.out.println("====================== Closure elements end ======================="); //$NON-NLS-1$
		}
	}
	
	public boolean hasClosure() {
		return (closureElements != null) && (closureElements.size() > 0);
	}
	
	/**
	 * define if the element is in the process element closure or not
	 * The process element closure contains all the process elements of the selected processes, 
	 * plus all the content elements those elements referenced.
	 */
	public boolean inClosure(MethodElement e) {
		
		if ( e == null ) {
			return false;
		}
		
		if ( !hasClosure() ) {
			return true;
		}
		
		if ( closureElements.contains(e) ) {
			return true;
		}
		
		if ( e instanceof ContentCategory ) {
			return true;
		}
		
		if ( !(e instanceof Guidance) ) {
			return false;
		}
		
		if ( e instanceof Practice 
				|| e instanceof Roadmap 
				|| e instanceof SupportingMaterial 
				|| e instanceof TermDefinition )
		{
			return true;
		}

		return false;
			
	}
	
	/**
	 * check if the element is discarded or not
	 * discarded elements will be treated as out side the configursation
	 * 
	 * @param owner MethodElement the owner of the element
	 * @param Object feature EStructuralFeature or OppositeFeature
	 * @param e MethodElement the element to be checked
	 */
	/* (non-Javadoc)
	 * @see com.ibm.rmc.library.layout.IContentValidator#isDiscarded(org.eclipse.epf.uma.MethodElement, java.lang.Object, org.eclipse.epf.uma.MethodElement)
	 */
	public boolean isDiscarded(MethodElement owner, Object feature, MethodElement e) {
	
		if ( owner == null ) {
			owner = defaultTarget;
		} else if ( defaultTarget != null && owner != defaultTarget ) {
			
			super.logWarning("Target mismatch" + LibraryUtil.getTypeName(owner) + "<--->" + LibraryUtil.getTypeName(defaultTarget)); //$NON-NLS-1$ //$NON-NLS-2$
		}
		
		if ( super.isDiscarded(owner, feature, e) ) {
			return true;
		}
		
		boolean inCls = inClosure(e);
		if (!inCls && !isFinalClosure ) {
			// if the closure is not final, then if the owner is in closure and the element is a Guidance, 
			// this element is also in closure
			inCls = inClosure(owner) && (e instanceof Guidance);
		}
		
		if ( !inCls ) {
			return true;
		}
		
//		// special handling for Tasks
//		if ( owner instanceof Task ) {
//			if ( feature == UmaPackage.eINSTANCE.getTask_AdditionallyPerformedBy() || 
//					feature == UmaPackage.eINSTANCE.getTask_MandatoryInput() ||
//					feature == UmaPackage.eINSTANCE.getTask_OptionalInput() || 
//					feature == UmaPackage.eINSTANCE.getTask_Output() ) {
//				List refs = (List)objReferences.get(owner);
//				if ( refs != null && !refs.contains(e) ) {
//					return true;
//				}
//			}
//		}
		
		return false;
	}
	
	
//	public void setElementReferences(Task element, List refs) {
//
//		List items = (List)objReferences.get(element);
//		if ( items == null ) {
//			items = new ArrayList();
//			objReferences.put(element, items);
//		}
//		
//		items.addAll(refs);		
//	}
	
	public void dispose() {
		
		if ( closureElements != null ) {
			closureElements.clear();
		}
		
//		if ( objReferences != null ) {
//			objReferences.clear();
//		}
		
		if ( closureLog != null ) {
			closureLog.close();
			closureLog = null;
		}
		
		super.dispose();
		
	}
	
	private void beginClosureLog() {
		closureLog.println("<table>");
		addLogEntry("<b>Element</b>", "<b>Referenced By</b>");
	}
	
	private void endClosureLog() {
		closureLog.println("</table>");
	}
	
	private void addLogEntry(String str1, String str2) {
		StringBuffer b = new StringBuffer();
		b.append("<tr><td>").append(str1).append("</td><td>").append(str2).append("</td></tr>");
		closureLog.println(b);
	}
	
	public void logReference(MethodElement owner, MethodElement e)
	{
		super.logReference(owner, e);
		if ( e instanceof Descriptor ) {
			return;
		}
		
		if ( !isFinalClosure && closureLog != null) {
			
			StringBuffer b = new StringBuffer();
			if ( owner instanceof BreakdownElement ) {
				b.append(getBreakdownElementLabel((BreakdownElement)owner));
			} else if ( owner != null ) {
				b.append(LibraryUtil.getTypeName(owner));
			}
			
			addLogEntry(LibraryUtil.getTypeName(e), b.toString());
		}
	}
	
	private StringBuffer getBreakdownElementLabel(BreakdownElement e) {
		StringBuffer b = new StringBuffer();
		b.append(LibraryUtil.getTypeName(e));

		while ( (e=e.getSuperActivities()) != null ) {
			b.insert(0, LibraryUtil.getTypeName(e) + "/");
		}
		
		return b;
	}
		
}
