/**
 * Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved.
 * Licensed under the terms of the Eclipse Public License (EPL).
 * Please see the license.txt included with this distribution for details.
 * Any modifications to this file must keep this entire header intact.
 */
/*
 * @author: fabioz
 * Created: January 2004
 */

package org.python.pydev.editor.actions;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.preference.IPreferenceStore;
import org.python.pydev.core.docutils.PySelection;
import org.python.pydev.editor.actions.PyFormatStd.FormatStd;
import org.python.pydev.editor.commentblocks.CommentBlocksPreferences;
import org.python.pydev.plugin.preferences.PydevPrefs;
import org.python.pydev.shared_core.SharedCorePlugin;
import org.python.pydev.shared_core.string.FastStringBuffer;
import org.python.pydev.shared_core.string.StringUtils;
import org.python.pydev.shared_core.structure.Tuple;

/**
 * Creates a comment block.  Comment blocks are slightly different than regular comments 
 * created in that they provide a distinguishing element at the beginning and end as a 
 * separator.  In this case, it is a string of <code>=======</code> symbols to strongly
 * differentiate this comment block.
 * 
 * @author Fabio Zadrozny
 * @author Parhaum Toofanian
 */
public class PyAddBlockComment extends AbstractBlockCommentAction {

    private boolean defaultClassNameBehaviour;
    private boolean defaultFunctionNameBehaviour;
    private FormatStd std;

    public PyAddBlockComment(FormatStd std) {
        //default
        this.std = std;
    }

    public PyAddBlockComment() {
        this(null);
    }

    /**
     * For tests: assigns the default values
     */
    PyAddBlockComment(FormatStd std, int defaultCols, boolean alignLeft, boolean classNameBehaviour,
            boolean functionNameBehaviour) {
        super(defaultCols, alignLeft);
        this.std = std;
        this.defaultClassNameBehaviour = classNameBehaviour;
        this.defaultFunctionNameBehaviour = functionNameBehaviour;
    }

    @Override
    protected void revealSelEndLine(PySelection ps) {
        getTextEditor().selectAndReveal(ps.getEndLine().getOffset(), 0);
    }

    protected boolean getUseClassNameBehaviour() {
        if (SharedCorePlugin.inTestMode()) {
            return defaultClassNameBehaviour;
        }

        IPreferenceStore prefs = PydevPrefs.getPreferenceStore();
        return prefs.getBoolean(CommentBlocksPreferences.MULTI_BLOCK_COMMENT_SHOW_ONLY_CLASS_NAME);
    }

    protected boolean getUseFunctionNameBehaviour() {
        if (SharedCorePlugin.inTestMode()) {
            return defaultFunctionNameBehaviour;
        }

        IPreferenceStore prefs = PydevPrefs.getPreferenceStore();
        return prefs.getBoolean(CommentBlocksPreferences.MULTI_BLOCK_COMMENT_SHOW_ONLY_FUNCTION_NAME);
    }

    /**
     * Performs the action with a given PySelection
     * 
     * @param ps Given PySelection
     * @return boolean The success or failure of the action
     */
    @Override
    public Tuple<Integer, Integer> perform(PySelection ps) {
        // What we'll be replacing the selected text with
        FastStringBuffer strbuf = new FastStringBuffer();
        FastStringBuffer tempBuffer = new FastStringBuffer();

        // If they selected a partial line, count it as a full one
        ps.selectCompleteLine();

        try {
            String fullCommentLine;
            String endLineDelim = ps.getEndLineDelim();

            int startLineIndex = ps.getStartLineIndex();
            int endLineIndex = ps.getEndLineIndex();

            boolean classBehaviour = false;
            if (startLineIndex == endLineIndex && getUseClassNameBehaviour()) {
                if (ps.isInClassLine()) {
                    //just get the class name
                    classBehaviour = true;
                }
            }

            boolean functionBehaviour = false;
            if (startLineIndex == endLineIndex && getUseFunctionNameBehaviour()) {
                if (ps.isInFunctionLine(false)) {
                    //just get the class name
                    functionBehaviour = true;
                }
            }

            // Start of block

            if (classBehaviour || functionBehaviour) {
                String line = ps.getLine(startLineIndex);

                int classIndex;
                int tokLen;

                if (classBehaviour) {
                    classIndex = line.indexOf("class ");
                    tokLen = 6;
                } else {
                    classIndex = line.indexOf("def ");
                    tokLen = 4;
                }

                fullCommentLine = getFullCommentLine(classIndex, tempBuffer);
                String spacesBefore;
                if (classIndex > 0) {
                    spacesBefore = line.substring(0, classIndex);
                } else {
                    spacesBefore = "";
                }

                strbuf.append(spacesBefore + "#").append(fullCommentLine).append(endLineDelim);
                String initialLine = line;
                line = line.substring(classIndex + tokLen);
                FastStringBuffer className = new FastStringBuffer();
                for (int i = 0; i < line.length(); i++) {
                    char cN = line.charAt(i);
                    if (Character.isJavaIdentifierPart(cN)) {
                        className.append(cN);
                    } else {
                        break;
                    }
                }

                strbuf.append(spacesBefore);
                strbuf.append("# ");
                strbuf.append(className);
                strbuf.append(endLineDelim);

                strbuf.append(spacesBefore);
                strbuf.append("#").append(fullCommentLine);
                strbuf.append(endLineDelim);
                strbuf.append(initialLine);

            } else {
                List<String> lines = new ArrayList<String>();

                int minCharsBefore = Integer.MAX_VALUE;
                for (int i = startLineIndex; i <= endLineIndex; i++) {
                    String line = ps.getLine(i);
                    minCharsBefore = Math.min(minCharsBefore, PySelection.getFirstCharPosition(line));
                    lines.add(line);
                }

                String firstLine = lines.get(0);
                String lastLine = lines.get(lines.size() - 1);

                String strBefore = firstLine.substring(0, minCharsBefore);
                fullCommentLine = getFullCommentLine(getLenOfStrConsideringTabEditorLen(strBefore), tempBuffer.clear());
                strbuf.append(strBefore).append("#").append(fullCommentLine).append(endLineDelim);

                String spacesInStartComment = null;
                FormatStd std = this.std != null ? this.std : PyFormatStd.getFormat(getPyEdit());
                if (std.spacesInStartComment != 0) {
                    if (std.spacesInStartComment < 0) {
                        //Negative means that we manage it manually!
                        spacesInStartComment = StringUtils.createSpaceString(1);

                    } else {
                        spacesInStartComment = StringUtils.createSpaceString(std.spacesInStartComment);
                    }
                }

                // For each line, comment them out
                for (int i = startLineIndex; i <= endLineIndex; i++) {
                    String line = ps.getLine(i);
                    strbuf.append(line.substring(0, minCharsBefore));
                    strbuf.append("#");
                    line = line.substring(minCharsBefore);
                    strbuf.append(spacesInStartComment);
                    strbuf.append(line);
                    strbuf.append(endLineDelim);
                }
                // End of block
                String strAfter = firstLine.substring(0, minCharsBefore);
                fullCommentLine = getFullCommentLine(getLenOfStrConsideringTabEditorLen(strAfter), tempBuffer.clear());
                strbuf.append(lastLine.substring(0, minCharsBefore)).append("#").append(fullCommentLine);
            }

            int startOffset = ps.getStartLine().getOffset();
            String str = strbuf.toString();
            // Replace the text with the modified information
            ps.getDoc().replace(startOffset, ps.getSelLength(), str);

            return new Tuple<Integer, Integer>(startOffset + str.length(), 0);
        } catch (Exception e) {
            e.printStackTrace();
            beep(e);
        }

        // In event of problems, return null
        return null;
    }

    @Override
    protected String getPreferencesNameForChar() {
        return CommentBlocksPreferences.MULTI_BLOCK_COMMENT_CHAR;
    }

    /**
     * Currently returns a string with the comment block.  
     * 
     * @return Comment line string, or a default one if Preferences are null
     */
    protected String getFullCommentLine(int subtract, FastStringBuffer buffer) {
        Tuple<Integer, Character> colsAndChar = getColsAndChar();
        int cols = colsAndChar.o1 - subtract;
        char c = colsAndChar.o2;

        buffer.clear();
        for (int i = 0; i < cols - 1; i++) {
            buffer.append(c);
        }
        return buffer.toString();
    }
}
