/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.netbeans.modules.csl.api;

import java.awt.event.ActionEvent;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import org.netbeans.api.editor.EditorActionNames;
import org.netbeans.editor.BaseAction;
import org.netbeans.modules.csl.editor.semantic.MarkOccurrencesHighlighter;
import org.netbeans.spi.editor.highlighting.HighlightsSequence;
import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer;
import org.openide.awt.StatusDisplayer;
import org.openide.util.NbBundle;

/**
 * This file is originally from Retouche, the Java Support 
 * infrastructure in NetBeans. I have modified the file as little
 * as possible to make merging Retouche fixes back as simple as
 * possible. 
 *
 * @todo The Java implementation changed to jumping to the
 *   END of identifiers in this integration:
 *    http://hg.netbeans.org/main/rev/8f417bdb256d
 *  to handle bug 136665 - should we do the same to be
 *  consistent?
 *
 * @author Vladimir Voskresensky
 * @deprecated use {@link CslActions#createGoToMarkOccurrencesAction(boolean) } instead.
 */
public final class GoToMarkOccurrencesAction extends BaseAction {

    private static final String prevActionName = EditorActionNames.gotoPrevOccurrence;
    private static final String nextActionName = EditorActionNames.gotoNextOccurrence;

    private final boolean next;

    public GoToMarkOccurrencesAction(boolean nextOccurrence) {
        super(getNameString(nextOccurrence));
        this.next = nextOccurrence;
        putValue(SHORT_DESCRIPTION, getDefaultShortDescription());
    }

    public void actionPerformed(ActionEvent evt, JTextComponent txt) {
        navigateToOccurence(next, txt);
    }

    @Override
    protected Object getDefaultShortDescription() {
        return NbBundle.getMessage(GoToMarkOccurrencesAction.class, getNameString(next));
    }
    
    private static String getNameString(boolean nextOccurrence) {
        return nextOccurrence ? nextActionName : prevActionName;
    }
    
    @SuppressWarnings("empty-statement")
    private static int findOccurrencePosition(boolean directionForward, Document doc, int curPos) {
        AbstractHighlightsContainer bag = MarkOccurrencesHighlighter.getHighlightsBag(doc);
        HighlightsSequence hs = bag.getHighlights(0, doc.getLength());

        if (hs.moveNext()) {
            if (directionForward) {
                int firstStart = hs.getStartOffset(), firstEnd = hs.getEndOffset();
                boolean hasElements = true;
                while (hs.getStartOffset() <= curPos && (hasElements = hs.moveNext()));

                if (hasElements && hs.getStartOffset() > curPos) {
                    // we found next occurrence
                    return hs.getStartOffset();
                } else if (!(firstEnd >= curPos && firstStart <= curPos)) {
                    // cyclic jump to first occurrence unless we already there
                    return firstStart;
                }
            } else {
                int current = hs.getStartOffset(), last;
                boolean stuck = false;
                do {
                    last = current;
                    current = hs.getStartOffset();
                } while (hs.getEndOffset() < curPos && (stuck = hs.moveNext()));

                if (last == current) {
                    // we got no options to jump, cyclic jump to last in file unless we already there
                    int lastSO = current;
                    int lastEO = Integer.MAX_VALUE;
                    while (hs.moveNext()) {
                        lastSO = hs.getStartOffset();
                        lastEO = hs.getEndOffset();
                    }
                    if (!(lastEO >= curPos && lastSO <= curPos)) {
                        return lastSO;
                    }
                } else if (stuck) {
                    // just move to previous occurence
                    return last;
                } else {
                    // it was last occurence in the file
                    return current;
                }
            }
        }
        return -1;
    }

    private static void navigateToOccurence(boolean next, JTextComponent txt) {
        if (txt != null && txt.getDocument() != null) {
            Document doc = txt.getDocument();
            int position = txt.getCaretPosition();
            int goTo = findOccurrencePosition(next, doc, position);
            if (goTo > 0) {
                txt.setCaretPosition(goTo);
            } else {
                StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(GoToMarkOccurrencesAction.class, "csl-no-marked-occurrence"));
            }
        }

    }    
}
