/*******************************************************************************
 * Copyright (c) 2009 Andrey Loskutov.
 * 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
 * Contributor:  Andrey Loskutov - initial API and implementation
 *******************************************************************************/

package de.loskutov.anyedit.actions;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelection;

import de.loskutov.anyedit.util.LineReplaceResult;
import de.loskutov.anyedit.util.TextReplaceResultSet;

public abstract class AbstractReplaceAction extends AbstractTextAction {

    /** contains possible error information during replace */
    protected boolean shouldStopReplace;

    @Override
    protected TextReplaceResultSet estimateActionRange(IDocument doc){
        TextReplaceResultSet result = new TextReplaceResultSet();
        if (doc == null || getEditor().getSelectionProvider() == null) {
            return result;
        }
        ISelection selection = getEditor().getSelectionProvider().getSelection();
        if(selection == null || !(selection instanceof ITextSelection)){
            return result;
        }
        ITextSelection textSelection = (ITextSelection)selection;
        if(textSelection.isEmpty()){
            return result;
        }
        result.setStartLine(textSelection.getStartLine());
        result.setStopLine(textSelection.getEndLine());
        return result;
    }

    @Override
    protected void doTextOperation(IDocument doc,
            String actionID, TextReplaceResultSet resultSet)
                    throws BadLocationException {

        ITextSelection textSelection = (ITextSelection) getEditor()
                .getSelectionProvider().getSelection();

        int actionKey = getActionKey(actionID);
        int startSelection = textSelection.getOffset();
        int stopSelection = startSelection + textSelection.getLength();

        int maxNbr = resultSet.getStartLine() + resultSet.getNumberOfLines();

        for (int i = resultSet.getStartLine(); i < maxNbr; i++) {

            IRegion lineInfo = doc.getLineInformation(i);

            boolean useStartLineOffset = lineInfo.getOffset() > startSelection;
            int startReplaceIndex;
            if(useStartLineOffset){
                startReplaceIndex = 0;
            } else {
                startReplaceIndex = startSelection - lineInfo.getOffset();
            }

            int stopReplace;
            boolean useStopLineOffset = lineInfo.getOffset() + lineInfo.getLength() > stopSelection;
            if(useStopLineOffset){
                stopReplace = stopSelection - lineInfo.getOffset();
            } else {
                stopReplace = lineInfo.getLength();
            }

            int rangeToReplace = stopReplace - startReplaceIndex;

            if(rangeToReplace <= 0){
                resultSet.add(null);
                continue;
            }

            String line = doc.get(
                    lineInfo.getOffset() + startReplaceIndex, rangeToReplace);
            if(line == null){
                resultSet.add(null);
                continue;
            }

            // call abstract replace operation
            String newText = performReplace(line, actionKey);

            if(shouldStopReplace) {
                shouldStopReplace = false;
                resultSet.clear();
                return;
            }

            boolean changed = !line.equals(newText);
            if(changed){
                LineReplaceResult result = new LineReplaceResult();
                result.startReplaceIndex = startReplaceIndex;
                result.rangeToReplace = rangeToReplace;
                result.textToReplace = newText;
                resultSet.add(result);
            } else {
                resultSet.add(null);
            }
        }
    }

    /**
     * Does custom replace operation.
     * @param line text to "replace"
     * @param actionKey any one action key as delivered by getActionKey(String)
     * method
     * @return "replace" result or old unchanged string, if replace was not performed
     */
    protected abstract String performReplace(String line, int actionKey);

    /**
     * Mapping between String actions and "smarter" custom int's for
     * inernal (in performReplace() method)  use only!
     * @param actionID action id <b>starts</b> with one of AbstractTextAction.ACTION_ID_
     * constants (it starts with the constant cause we have multiple actions
     * there which are used once for key bindings and once for editor actions).
     * @return any one int value for given action
     */
    protected abstract int getActionKey(String actionID);

}
