/*******************************************************************************
 * Copyright (c) 2011 Gil Barash
 * 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:
 *     Gil Barash - Initial implementation
 *******************************************************************************/
package org.eclipse.cdt.codan.internal.checkers.ui.quickfix;

import org.eclipse.cdt.codan.core.cxx.CxxAstUtils;
import org.eclipse.cdt.codan.internal.checkers.ui.CheckersUiActivator;
import org.eclipse.cdt.codan.ui.AbstractAstRewriteQuickFix;
import org.eclipse.cdt.core.dom.ast.IASTBreakStatement;
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeSelector;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.IASTSwitchStatement;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IRegion;
import org.eclipse.ltk.core.refactoring.Change;

public class CaseBreakQuickFixBreak extends AbstractAstRewriteQuickFix {
	@Override
	public boolean isApplicable(IMarker marker) {
		int line = marker.getAttribute(IMarker.LINE_NUMBER, 0) - 1;
		if (line < 0)
			return false;
		return true;
	}

	@Override
	public String getLabel() {
		return QuickFixMessages.CaseBreakQuickFixBreak_Label;
	}

	protected IASTStatement getStmtBeforeBreak(IMarker marker, IASTTranslationUnit ast) throws BadLocationException {
		int line = marker.getAttribute(IMarker.LINE_NUMBER, 0) - 1;
		if (line < 0)
			return null;
		IRegion lineInformation = getDocument().getLineInformation(line);
		IASTNodeSelector nodeSelector = ast.getNodeSelector(null);
		IASTNode containedNode = nodeSelector.findFirstContainedNode(lineInformation.getOffset(), lineInformation.getLength());
		IASTNode beforeBreakNode = null;
		if (containedNode != null) {
			beforeBreakNode = CxxAstUtils.getEnclosingStatement(containedNode);
		} else {
			beforeBreakNode = nodeSelector.findEnclosingNode(lineInformation.getOffset(), lineInformation.getLength());
		}
		if (beforeBreakNode instanceof IASTCompoundStatement) {
			while (beforeBreakNode != null) {
				if (beforeBreakNode.getParent() instanceof IASTCompoundStatement
						&& beforeBreakNode.getParent().getParent() instanceof IASTSwitchStatement) {
					return (IASTStatement) beforeBreakNode;
				}
				beforeBreakNode = beforeBreakNode.getParent();
			}
		}
		if (beforeBreakNode instanceof IASTStatement)
			return (IASTStatement) beforeBreakNode;
		return null;
	}

	@Override
	public void modifyAST(IIndex index, IMarker marker) {
		try {
			IASTTranslationUnit ast = getTranslationUnitViaEditor(marker).getAST(index, ITranslationUnit.AST_SKIP_INDEXED_HEADERS);
			IASTStatement beforeBreak = getStmtBeforeBreak(marker, ast);
			if (beforeBreak != null && beforeBreak.getParent() instanceof IASTCompoundStatement) {
				IASTCompoundStatement enclosingStatement;
				IASTStatement after;
				if (beforeBreak instanceof IASTCompoundStatement) {
					// Case body is enclosed in braces. Add 'break' as last statement inside braces.
					enclosingStatement = (IASTCompoundStatement) beforeBreak;
					after = null;
				} else {
					enclosingStatement = (IASTCompoundStatement) beforeBreak.getParent();
					after = getStatementAfter(beforeBreak);
				}
				ASTRewrite r = ASTRewrite.create(enclosingStatement.getTranslationUnit());
				IASTBreakStatement breakStatement = ast.getASTNodeFactory().newBreakStatement();
				r.insertBefore(enclosingStatement, after, breakStatement, null);
				Change c = r.rewriteAST();
				c.perform(new NullProgressMonitor());
			}
		} catch (CoreException e) {
			CheckersUiActivator.log(e);
		} catch (BadLocationException e) {
			CheckersUiActivator.log(e);
		}
	}

	private IASTStatement getStatementAfter(IASTStatement beforeBreak) {
		IASTCompoundStatement enclosingStatement = (IASTCompoundStatement) beforeBreak.getParent();
		IASTStatement after = null;
		IASTStatement[] statements = enclosingStatement.getStatements();
		for (int i = 0; i < statements.length; i++) {
			IASTStatement st = statements[i];
			if (st == beforeBreak) {
				if (i < statements.length - 1) {
					after = statements[i + 1];
					break;
				}
			}
		}
		return after;
	}
}
