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

/**
 * 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 EE2EIndentXML extends EE2EIndentBehavior {

    /** constractor */
    public EE2EIndentXML(int indentSize, boolean emptyLine) {
        this.indentSize = indentSize;
        this.emptyLine = emptyLine;
    }

    /**
     * XML indent main function.
     * @param document
     * @param offset
     * @throws BadLocationException
     */
    protected void doIndent(int offset, IDocument document)
        throws BadLocationException {
        if (this.emptyLine) {
            if (this.doEmptyLine(offset, document)) {
                return;
            }
        }
        indentCommandPost(offset, document);
    }

    /** Empty line check */
    private boolean doEmptyLine(int offset, IDocument document)
        throws BadLocationException {
        int nextOffset = EE2ELispBase.get().beginningOfLine(offset, document);
        for (int i = nextOffset; i < document.getLength(); i++) {
            if ((' ' != document.getChar(i))
                && ('\t' != document.getChar(i))
                && ('\r' != document.getChar(i))
                && ('\n' != document.getChar(i))) {
                break;
            }
            if ((' ' == document.getChar(i))
                || ('\t' == document.getChar(i))) {
                continue;
            }
            // Empty Line Appeard !!
            EE2ELispBase.get().indentTo(offset, 0, document);
            return true;
        }
        return false;
    }

    /**
     * @param offset
     * @param document
     * @throws BadLocationException
    */
    private void indentCommandPost(int offset, IDocument document)
        throws BadLocationException {

        //
        // 計算の煩雑化を避けるため行先頭へまず移動
        offset = EE2ELispBase.get().beginningOfLine(offset, document);

        // もし先頭行ならばindent 0
        if ((offset <= 0)
            || (EE2ELispBase
                .get()
                .skipCharsBackward(offset - 1, " \t\r\n", document)
                <= 0)) {
            EE2ELispBase.get().indentTo(offset, 0, document);
            return;
        }
        // 一つ前の最終文字に移動
        int endOffset =
            EE2ELispBase.get().skipCharsBackward(
                offset - 1,
                " \t\r\n",
                document);
        if (document.getLength() <= endOffset) {
            EE2ELispBase.get().indentTo(offset, 0, document);
            return;
        }
        // 基準インデント行を取得
        int startOffset =
            EE2ELispBase.get().beginningOfLine(endOffset, document);
        // 基準インデント値を取得
        int stndardLen = EE2ELispBase.get().indentLen(startOffset, document);
        // 基準インデント行の空白を除いた先頭に移動
        startOffset =
            EE2ELispBase.get().skipCharsForward(
                startOffset,
                " \t",
                document,
                endOffset)
                + 1;
        if (startOffset <= 1) {
            startOffset = 0;
        }
        //
        int nowOffset = startOffset;
        int mark = 0;
        while (true) {
            nowOffset =
                EE2ELispBase.get().findCharsForward(
                    nowOffset,
                    "<>",
                    document,
                    endOffset);
            char nowChar = document.getChar(nowOffset);
            if (('<' != nowChar) && ('>' != nowChar)) {
                break;
            }
            //基準インデント行でのチェック
            mark += doPrependIndentMark(startOffset, nowOffset, document);
            nowOffset++;
        }
        // 壊れたインデントの清算
        if (mark < -1) {
            mark = -1;
        }
        if (1 < mark) {
            mark = 1;
        }
        // 不整合チェック
        mark += this.checkUnbarance(startOffset, endOffset, document);
        // 目標インデント行のチェック
        mark += doTargetMark(offset, document);
        EE2ELispBase.get().indentTo(
            offset,
            stndardLen + (mark * this.indentSize),
            document);
    }

    private int doPrependIndentMark(
        int startOffset,
        int nowOffset,
        IDocument document)
        throws BadLocationException {
        char startChar = document.getChar(startOffset);
        char startNextChar = document.getChar(startOffset + 1);
        char backChar;
        if (nowOffset <= 0) {
            backChar = ' ';
        } else {
            backChar = document.getChar(nowOffset - 1);
        }
        char nowChar = document.getChar(nowOffset);
        char nextChar = document.getChar(nowOffset + 1);
        int mark = 0;
        if ('<' == nowChar) {
            if ('/' == nextChar) {
                mark += 1;
                if (('<' != startChar) || ('/' != startNextChar)) {
                    mark -= 1;
                }
            } else if (isForWord(nextChar)) {
                mark += 1;
            } else {
                mark += 2;
            }
        } else if (('>' == nowChar)) {
            if (!isBackWord(backChar)) {
                mark -= 1;
            } else {
                mark -= 2;
            }
        }
        return mark;
    }
    private boolean isForWord(char forWord) {
        return ('!' == forWord) || ('?' == forWord);
    }
    private boolean isBackWord(char backChar) {
        return ('/' == backChar) || ('%' == backChar) || (']' == backChar);
    }
    private int doTargetMark(int offset, IDocument document)
        throws BadLocationException {
        int nowOffset =
            EE2ELispBase.get().skipCharsForward(offset, " \t", document) + 1;
        char startChar = document.getChar(nowOffset);
        char startNextChar = document.getChar(nowOffset + 1);
        if (('<' == startChar) && ('/' == startNextChar)) {
            return -1;
        }
        return 0;
    }
    private int checkUnbarance(
        int startOffset,
        int endOffset,
        IDocument document)
        throws BadLocationException {
        int count = 0;
        int last = startOffset;
        for (int i = startOffset; i <= endOffset; i++) {
            char ch = document.getChar(i);
            if ('<' == ch) {
                count++;
            } else if ('>' == ch) {
                count--;
                last = i;
            }
        }
        if (count < 0) {
            char lastBackCh = document.getChar(last - 1);
            if (isBackWord(lastBackCh)) {
                return 0;
            }
            startOffset--;
            startOffset =
                EE2ELispBase.get().findCharsBackward(
                    startOffset,
                    "<",
                    document);
            //char nowChar = document.getChar(startOffset);
            char nextChar = document.getChar(startOffset + 1);
            if (isForWord(nextChar)) {
                return 0;
            }
            return +1;
        }
        return 0;
    }

    /** indent offset */
    private int indentSize;
    /** When this flag stands, it does not indent in an empty line. */
    private boolean emptyLine;
}