package jp.sourceforge.projects.ee2e.core.editors;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;

import jp.sourceforge.projects.ee2e.core.EE2EPlugin;

import org.eclipse.jface.preference.IPreferenceStore;

/**
 * To change this generated comment edit the template variable "typecomment":
 * Window&gt;Preferences&gt;Java&gt;Templates.
 * To enable and disable the creation of type comments go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation.
 *
 * @author Toshikazu Ando [ ando@park.ruru.ne.jp ]
 */
public class EE2EIndentJava extends EE2EIndentBehavior {
    /** constractor */
    public EE2EIndentJava(int indentLen) {
        //
        this.indentLen = indentLen;
    }

    /**
     * @see jp.sourceforge.projects.ee2e.core.editors.EE2EMenuBehavior#tabDestroyer(AbstractTextEditor, IDocument, ITextSelection)
     */
    protected void doIndent(int argumentOffset, IDocument document)
        throws BadLocationException {
        //
        int offset =
            EE2ELispBase.get().beginningOfLine(argumentOffset, document);

        // 先頭が #から始まったらindent 0
        if (this.doSharpSheck(offset, document)) {
            EE2ELispBase.get().indentTo(offset, 0, document);
            return;
        }

        /* "javadoc" indent check! */
        int len;
        int ans = checkDocument(offset, document);
        if (0 <= ans) {
            len = ans;
            if (!isMyLineDocument(offset, document)) {
                EE2ELispBase.get().indentTo(offset, len, document);
            }
            EE2ELispBase.get().indentTo(offset, len + 1, document);
            return;
        }
        if (this.isRedundantCircle()) {
            ans = checkCercle(offset, document);
            if (0 <= ans) {
                len = ans;
                EE2ELispBase.get().indentTo(offset, len, document);
                return;
            }
        }

        // 一つ前のchar位置を取得する
        // 先頭行の一つ前の文字から検索するので offset - 1
        int oneBefore = getPrependChar(offset - 1, document, 0);
        if (oneBefore <= 0) {
            EE2ELispBase.get().indentTo(offset, 0, document);
            return;
        }

        len = 0;

        // 一つ前のcharが ';' か '}' でなければ + 4
        char targetChar = document.getChar(oneBefore);
        if ((targetChar != ';') && (targetChar != '}')) {
            len += this.indentLen;
        }

        // target line が ':' で終わったら -4
        int lastOffset = EE2ELispBase.get().endOfLine(offset, document);
        lastOffset = getPrependChar(lastOffset + 1, document, offset);
        if (0 <= lastOffset) {
            targetChar = document.getChar(lastOffset);
            if (targetChar == ':') {
                len -= this.indentLen;
            }
        }

        // target line が '}' で始まったら -4
        int firstOffset =
            EE2ELispBase.get().skipCharsForward(offset, " \t", document) + 1;
        if (firstOffset < document.getLength()) {
            targetChar = document.getChar(firstOffset);
            if ((targetChar == '}')
                || (targetChar == ']')
                || (targetChar == ')')) {
                len -= this.indentLen;
            }
        }

        // get base line offset.
        int baseLineOffset = this.getBaseLineOffset(oneBefore, document);
        len += baseLineOffset;

        // compact circle.
        if (len < 0) {
            len = 0;
        }
        EE2ELispBase.get().indentTo(offset, len, document);
    }
    protected int getBaseLineOffset(int offset, IDocument document)
        throws BadLocationException {
        int baseLineOffset =
            EE2ELispBase.get().beginningOfLine(offset, document);
        int oneBefore = offset;
        char targetChar;
        boolean flag = false;
        while (0 <= baseLineOffset) {
            oneBefore = this.getPrependChar(oneBefore - 1, document, 0);
            if (oneBefore < 0) {
                break;
            }
            if (!flag) {
                targetChar = document.getChar(oneBefore);
                if ((targetChar == ';')
                    || (targetChar == ':')
                    || (targetChar == '{')
                    || (targetChar == '}')) {
                    flag = true;
                }
            }
            if (flag) {
                if (this.isNotInTheCircle(baseLineOffset, offset, document)) {
                    break;
                }
            }
            baseLineOffset = oneBefore;
            oneBefore = EE2ELispBase.get().beginningOfLine(oneBefore, document);
        }
        return EE2ELispBase.get().indentLen(baseLineOffset, document);
    }
    protected boolean isNotInTheCircle(
        int oneBefore,
        int end,
        IDocument document)
        throws BadLocationException {
        int start = EE2ELispBase.get().beginningOfLine(oneBefore, document);
        int pos = 0;
        boolean toriger = false;
        for (int offset = start; offset <= end; offset++) {
            if (this.isQuoteTorigerCountCheck(offset, document)) {
                if (toriger) {
                    toriger = false;
                } else {
                    toriger = true;
                }
            }
            if (toriger) {
                continue;
            }
            char targetChar = document.getChar(offset);
            if ((targetChar == ')') || (targetChar == ']')) {
                pos++;
            } else if ((targetChar == '(') || (targetChar == '[')) {
                pos--;
            }
        }
        return pos <= 0;
    }

    /** ドキュメント内であると動作する */
    protected int checkDocument(int offset, IDocument document)
        throws BadLocationException {
        if (offset < 2) {
            return -1;
        }
        for (int i = offset - 2; 1 <= i; i--) {
            char ch1 = document.getChar(i - 1);
            char ch2 = document.getChar(i);
            if ((ch1 == '*') && (ch2 == '/')) {
                return -1;
            }
            if ((ch1 == '/') && (ch2 == '*')) {
                int ans = EE2ELispBase.get().indentLen(i, document);
                return ans;
            }
        }
        return -1;
    }

    protected boolean isMyLineDocument(int offset, IDocument document)
        throws BadLocationException {
        int start = EE2ELispBase.get().beginningOfLine(offset, document);
        int next = EE2ELispBase.get().skipCharsForward(start, " \t", document);
        next++;
        if (document.getLength() <= next) {
            return false;
        }
        char ch = document.getChar(next);
        if (ch == '*') {
            return true;
        }
        return false;
    }

    /** #で始まる行であれば true */
    private boolean doSharpSheck(int offset, IDocument document)
        throws BadLocationException {
        int next =
            EE2ELispBase.get().skipCharsForward(offset, " \t\r\n", document);
        next++;
        if (document.getLength() <= next) {
            return false;
        }
        char target = document.getChar(next);
        if (target == '#') {
            return true;
        }
        return false;
    }

    /** コメントをスキップしてそのオフセット値を返す */
    private int doCommentSkipOffset(int offset, IDocument document)
        throws BadLocationException {
        int before = offset - 1;
        if (document.getLength() <= offset) {
            return offset;
        }
        for (int nowOffset = before; 0 <= nowOffset; nowOffset--) {
            char ch1;
            char ch2;
            ch1 = document.getChar(nowOffset);
            ch2 = document.getChar(nowOffset + 1);
            if ((ch1 == '*') && (ch2 == '/')) {
                return offset;
            } else if ((ch1 == '/') && (ch2 == '*')) {
                return nowOffset;
            }
        }
        return offset;
    }

    /** 一つ前の字句を取得する */
    protected int getPrependChar(int offset, IDocument document, int min)
        throws BadLocationException {
        int oneBefore = offset;
        int target = -1;
        boolean commentTriger = false;
        while ((min < oneBefore) && (target < 0)) {
            oneBefore = this.doCommentSkipOffset(oneBefore, document);
            if (document.getLength() <= oneBefore) {
                return document.getLength() - 1;
            }
            boolean toriger = false;
            for (int commentOffset =
                EE2ELispBase.get().beginningOfLine(oneBefore, document);
                commentOffset < oneBefore + 1;
                commentOffset++) {
                char ch = document.getChar(commentOffset);
                if (ch == '#') {
                    toriger = true;
                }
                if ((ch != ' ')
                    && (ch != '\t')
                    && (ch != '\r')
                    && (ch != '\n')) {
                    break;
                }
            }
            if (toriger) {
                oneBefore =
                    EE2ELispBase.get().beginningOfLine(oneBefore, document);
                oneBefore--;
                continue;
            }
            toriger = false;
            boolean charTriger = false;
            int begin = EE2ELispBase.get().beginningOfLine(oneBefore, document);
            for (int commentOffset = oneBefore;
                begin <= commentOffset;
                commentOffset--) {
                if (this.isQuoteTorigerCountCheck(commentOffset, document)) {
                    toriger = !toriger;
                }
                if (toriger) {
                    continue;
                }
                char ch = document.getChar(commentOffset);
                char ch2 = (char) 0;
                if ((commentOffset + 1) < document.getLength()) {
                    ch2 = document.getChar(commentOffset + 1);
                }
                if (((ch == '/') && (ch2 == '*'))) {
                    commentTriger = false;
                    continue;
                }
                if (commentTriger) {
                    continue;
                }
                if (((ch == '*') && (ch2 == '/'))) {
                    if((target - 1) == commentOffset) {
                        target = -1;
                        charTriger = false;
                    }
                    commentTriger = true;
                    continue;
                }
                if (((ch == '/') && (ch2 == '/'))) {
                    target = -1;
                    charTriger = false;
                    continue;
                }
                if ((!charTriger)
                    && (ch != ' ')
                    && (ch != '\t')
                    && (ch != '\r')
                    && (ch != '\n')
                    && (ch != '\\')) { // C MACRO
                    target = commentOffset;
                    charTriger = true;
                }
            }
            if (0 <= target) {
                break;
            }
            oneBefore = EE2ELispBase.get().beginningOfLine(oneBefore, document);
            oneBefore--;
        }
        return target;
    }
    protected int checkCercle(int argOffset, IDocument document)
        throws BadLocationException {
        int ans = -1;
        int pos = 0;
        boolean toriger = false;
        for (int offset = argOffset; 0 <= offset; offset--) {
            if (this.isQuoteTorigerCountCheck(offset, document)) {
                if (toriger) {
                    toriger = false;
                } else {
                    toriger = true;
                }
            }
            if (toriger) {
                continue;
            }
            char ch = document.getChar(offset);
            if ((ch == '{') || (ch == '}')) {
                break;
            }
            if ((ch == ')') || (ch == ']')) {
                pos++;
                continue;
            }
            if ((ch == '(') || (ch == '[')) {
                pos--;
                if (pos < 0) {
                    int start =
                        EE2ELispBase.get().beginningOfLine(offset, document);
                    ans = offset - start + 1;
                    break;
                }
                continue;
            }
        }
        return ans;
    }
    protected boolean isRedundantCircle() {
        IPreferenceStore store = EE2EPlugin.getDefault().getPreferenceStore();
        return store.getBoolean(EE2EPlugin.P_COMPACT_INDENT);
    }

    /**  1ライン行のQuoteの数が奇数ならチェックしない  */
    protected boolean isQuoteTorigerCountCheck(int offset, IDocument document)
        throws BadLocationException {
        boolean ans = isQuoteToriger(offset,document);
        int lineOffset = EE2ELispBase.get().beginningOfLine(offset, document);
        int endLineOffset = EE2ELispBase.get().endOfLine(lineOffset, document);
        int count = 0;
        if (!ans) {
            return ans;
        }
        for (;lineOffset < endLineOffset ; lineOffset++) {
            if (this.isQuoteToriger(lineOffset,document)) {
                count++;
            }
        }
        if ((count % 2) != 0) {
            return false;
        }
        return true;
    }

    /**  /[\\\\]*;/   */
    protected boolean isQuoteToriger(int offset, IDocument document)
        throws BadLocationException {
        if ((offset < 0) || (document.getLength() <= offset)) {
            return false;
        }
        char ch = document.getChar(offset);
        boolean ans = (ch == '\'') || (ch == '\"');
        if (!ans) {
            return false;
        }
        int count = 0;
        offset--;
        for (; 0 <= offset; offset--) {
            ch = document.getChar(offset);
            if (ch != '\\') {
                break;
            }
            count++;
        }
        return (count % 2) == 0;
    }
    private int indentLen;
}
