/*******************************************************************************
 * Copyright (c) 2000, 2008 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
 *******************************************************************************/
package org.eclipse.wst.jsdt.internal.corext.codemanipulation;

import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBufferManager;
import org.eclipse.core.filebuffers.LocationKind;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
import org.eclipse.wst.jsdt.core.IField;
import org.eclipse.wst.jsdt.core.IJavaScriptElement;
import org.eclipse.wst.jsdt.core.IJavaScriptProject;
import org.eclipse.wst.jsdt.core.IMember;
import org.eclipse.wst.jsdt.core.IFunction;
import org.eclipse.wst.jsdt.core.IType;
import org.eclipse.wst.jsdt.core.ITypeHierarchy;
import org.eclipse.wst.jsdt.core.JavaScriptModelException;
import org.eclipse.wst.jsdt.core.Signature;
import org.eclipse.wst.jsdt.internal.corext.dom.TokenScanner;
import org.eclipse.wst.jsdt.internal.corext.util.MethodOverrideTester;
import org.eclipse.wst.jsdt.internal.corext.util.Strings;
import org.eclipse.wst.jsdt.internal.corext.util.SuperTypeHierarchyCache;
import org.eclipse.wst.jsdt.internal.ui.JavaUIStatus;
import org.eclipse.wst.jsdt.ui.CodeGeneration;

/**
*
* Provisional API: This class/interface is part of an interim API that is still under development and expected to
* change significantly before reaching stability. It is being made available at this early stage to solicit feedback
* from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken
* (repeatedly) as the API evolves.
*/
public class AddJavaDocStubOperation implements IWorkspaceRunnable {
	
	private IMember[] fMembers;
	
	public AddJavaDocStubOperation(IMember[] members) {
		super();
		fMembers= members;
	}

	private String createTypeComment(IType type, String lineDelimiter) throws CoreException {
		String[] typeParameterNames= StubUtility.getTypeParameterNames(type.getTypeParameters());
		return CodeGeneration.getTypeComment(type.getJavaScriptUnit(), type.getTypeQualifiedName('.'), typeParameterNames, lineDelimiter);
	}		
	
	private String createMethodComment(IFunction meth, String lineDelimiter) throws CoreException {
		IType declaringType= meth.getDeclaringType();
		
		IFunction overridden= null;
		if (!meth.isConstructor() && declaringType!=null) {
			ITypeHierarchy hierarchy= SuperTypeHierarchyCache.getTypeHierarchy(declaringType);
			MethodOverrideTester tester= new MethodOverrideTester(declaringType, hierarchy);
			overridden= tester.findOverriddenMethod(meth, true);
		}
		return CodeGeneration.getMethodComment(meth, overridden, lineDelimiter);
	}
	
	private String createFieldComment(IField field, String lineDelimiter) throws JavaScriptModelException, CoreException {
		String typeName= Signature.toString(field.getTypeSignature());
		String fieldName= field.getElementName();
		return CodeGeneration.getFieldComment(field.getJavaScriptUnit(), typeName, fieldName, lineDelimiter);
	}		
		
	/**
	 * @return Returns the scheduling rule for this operation
	 */
	public ISchedulingRule getScheduleRule() {
		return ResourcesPlugin.getWorkspace().getRoot();
	}

	/**
	 * Runs the operation.
	 * @throws OperationCanceledException Runtime error thrown when operation is cancelled.
	 */	
	public void run(IProgressMonitor monitor) throws CoreException, OperationCanceledException {
		if (monitor == null) {
			monitor= new NullProgressMonitor();
		}
		if (fMembers.length == 0) {
			return;
		}
		try {
			monitor.beginTask(CodeGenerationMessages.AddJavaDocStubOperation_description, fMembers.length + 2); 

			addJavadocComments(monitor);
		} finally {
			monitor.done();
		}
	}
	/* moved this so we this can be re-used in web component */
	
	protected IDocument getDocument(IJavaScriptUnit cu, IProgressMonitor monitor) throws CoreException  {
		ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager();
		IPath path= cu.getPath();
		try {
		manager.connect(path, LocationKind.IFILE, new SubProgressMonitor(monitor, 1));
			return manager.getTextFileBuffer(path).getDocument();
		
			
		}finally {
			manager.disconnect(path, LocationKind.IFILE,new SubProgressMonitor(monitor, 1));
		}
	}
	
	private void addJavadocComments(IProgressMonitor monitor) throws CoreException {
		IJavaScriptUnit cu= fMembers[0].getJavaScriptUnit();
		
		
		try {
			IDocument document= getDocument(cu,monitor);
			
			String lineDelim= TextUtilities.getDefaultLineDelimiter(document);
			MultiTextEdit edit= new MultiTextEdit();
			
			for (int i= 0; i < fMembers.length; i++) {
				IMember curr= fMembers[i];
				int memberStartOffset= getMemberStartOffset(curr, document);
				
				String comment= null;
				switch (curr.getElementType()) {
					case IJavaScriptElement.TYPE:
						comment= createTypeComment((IType) curr, lineDelim);
						break;
					case IJavaScriptElement.FIELD:
						comment= createFieldComment((IField) curr, lineDelim);	
						break;
					case IJavaScriptElement.METHOD:
						comment= createMethodComment((IFunction) curr, lineDelim);
						break;
				}
				if (comment == null) {
					StringBuffer buf= new StringBuffer();
					buf.append("/**").append(lineDelim); //$NON-NLS-1$
					buf.append(" *").append(lineDelim); //$NON-NLS-1$
					buf.append(" */").append(lineDelim); //$NON-NLS-1$
					comment= buf.toString();						
				} else {
					if (!comment.endsWith(lineDelim)) {
						comment= comment + lineDelim;
					}
				}
				
				final IJavaScriptProject project= cu.getJavaScriptProject();
				IRegion region= document.getLineInformationOfOffset(memberStartOffset);
				
				String line= document.get(region.getOffset(), region.getLength());
				String indentString= Strings.getIndentString(line, project);
				
				String indentedComment= Strings.changeIndent(comment, 0, project, indentString, lineDelim);

				edit.addChild(new InsertEdit(memberStartOffset, indentedComment));

				monitor.worked(1);
			}
			edit.apply(document); // apply all edits
		} catch (BadLocationException e) {
			throw new CoreException(JavaUIStatus.createError(IStatus.ERROR, e));
		} finally {
			
		}
	}

	private int getMemberStartOffset(IMember curr, IDocument document) throws JavaScriptModelException {
		int offset= curr.getSourceRange().getOffset();
		TokenScanner scanner= new TokenScanner(document, curr.getJavaScriptProject());
		try {
			return scanner.getNextStartOffset(offset, true); // read to the first real non comment token
		} catch (CoreException e) {
			// ignore
		}
		return offset;
	}
		
}
