/*******************************************************************************
 * Copyright (c) 2010 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.wst.sse.ui.internal.comment;

import java.util.List;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
import org.eclipse.wst.sse.ui.internal.Logger;

/**
 * <p>Represents a Block Comment commenting strategy</p>
 */
public class BlockCommentingStrategy extends CommentingStrategy {
	/** the prefix of the block comment associated with this strategy */
	private String fPrefix;
	
	/** the suffix of the block comment associated with this strategy */
	private String fSuffix;
	
	/**
	 * @param prefix the prefix of the block comment associated with this strategy
	 * @param suffix the suffix of the block comment associated with this strategy
	 */
	public BlockCommentingStrategy(String prefix, String suffix) {
		super();
		this.fPrefix = prefix;
		this.fSuffix = suffix;
	}

	/**
	 * <p>When applying a block comment it also removes any block comments associated
	 * with this strategy that would now be enclosed by the new block comment</p>
	 * 
	 * @see org.eclipse.wst.sse.ui.internal.comment.CommentingStrategy#apply(
	 * 	org.eclipse.wst.sse.core.internal.provisional.IStructuredModel, int, int)
	 */
	public void apply(IStructuredDocument document, int offset, int length) throws BadLocationException {
		int commentPrefixOffset = offset;
		int commentSuffixOffset = commentPrefixOffset + length;
		
		try {
			document.replace(commentSuffixOffset, 0, " " + this.fSuffix); //$NON-NLS-1$
			this.remove(document, commentPrefixOffset + this.fPrefix.length(), length, false);
			document.replace(commentPrefixOffset, 0, this.fPrefix + " "); //$NON-NLS-1$
		}
		catch (BadLocationException e) {
			Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e);
		}
	}

	/**
	 * @see org.eclipse.wst.sse.ui.internal.comment.CommentingStrategy#remove(
	 * 	org.eclipse.jface.text.IDocument, int, int)
	 */
	public void remove(IStructuredDocument document, int offset, int length, boolean removeEnclosing) throws BadLocationException {
		IRegion region = new Region(offset, length);
		ITypedRegion[] typedRegions = document.computePartitioning(region.getOffset(), region.getLength());
		List commentRegions = this.getAssociatedCommentedRegions(typedRegions);
		
		//remove in reverse order as to not effect offset of other regions
		for(int i = commentRegions.size()-1; i >= 0; --i) {
			try {
				//get the comment region
				ITypedRegion typedRegion = (ITypedRegion)commentRegions.get(i);
				IRegion commentRegion = new Region(typedRegion.getOffset(), typedRegion.getLength());

				/* because of the nature of structured regions the comment region could actually be a
				 * sub region that needs to be drilled down too
				 */
				if(!this.alreadyCommenting(document, commentRegion)) {
					IStructuredDocumentRegion structuredRegion =
						document.getRegionAtCharacterOffset(commentRegion.getOffset());
					
					commentRegion = new Region(structuredRegion.getStartOffset(), structuredRegion.getLength());
					
					if(!this.alreadyCommenting(document, commentRegion)) {
						ITextRegion enclosedRegion = structuredRegion.getRegionAtCharacterOffset(typedRegion.getOffset());
						int enclosedOffset = structuredRegion.getStartOffset(enclosedRegion);
						commentRegion = new Region(enclosedOffset, structuredRegion.getTextEndOffset(enclosedRegion)-enclosedOffset);
					}
				}
				
				//at this point should have found the comment region, if not there is an issue
				if(this.alreadyCommenting(document, commentRegion)) {
					String regionContent = document.get(commentRegion.getOffset(), commentRegion.getLength());
					
					//if found the comment prefix and suffix then uncomment, otherwise log error
					int commentPrefixOffset = commentRegion.getOffset() + regionContent.indexOf(this.fPrefix);
					int commentSuffixOffset = commentRegion.getOffset();
					commentSuffixOffset += regionContent.lastIndexOf(this.fSuffix);
					
					//remove comment block depending on if its an enclosing comment block and weather that is allowed
					if(removeEnclosing || (commentPrefixOffset >= offset && commentSuffixOffset <= offset+length)) {
						uncomment(document, commentPrefixOffset, this.fPrefix, commentSuffixOffset, this.fSuffix);
					}
				} else {
					Logger.log(Logger.ERROR,
							"BlockCommentingStrategy#remove could not find the commenting region to remove"); //$NON-NLS-1$
				}
			} catch(BadLocationException e) {
				Logger.logException("This should only ever happen if something has gone wrong with the partitioning", e); //$NON-NLS-1$
			}
		}
	}
	
	/**
	 * <p>A region is already commented by this strategy if it starts with the strategy's associated
	 * prefix and ends with its associated suffix, ignoring any trailing or leading whitespace</p>
	 * 
	 * @see org.eclipse.wst.sse.ui.internal.comment.CommentingStrategy#alreadyCommenting(
	 * 	org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IRegion)
	 */
	public boolean alreadyCommenting(IStructuredDocument document, IRegion region) throws BadLocationException {
		String regionContent = document.get(region.getOffset(), region.getLength()).trim();
		return regionContent.startsWith(this.fPrefix) && regionContent.endsWith(this.fSuffix);
	}
	
	/**
	 * @see org.eclipse.wst.sse.ui.internal.comment.CommentingStrategy#clone()
	 */
	public Object clone() {
		return new BlockCommentingStrategy(this.fPrefix, this.fSuffix);
	}
}