/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
 *
 * 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 com.android.ide.eclipse.adt.internal.editors;

import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.AdtProjectTest;

import org.eclipse.core.resources.IFile;
import org.eclipse.jface.text.DocumentCommand;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.swt.graphics.Point;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;

@SuppressWarnings("javadoc")
public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest {

    public void checkInsertNewline(String before, String after) throws Exception {
        AndroidXmlAutoEditStrategy s = new AndroidXmlAutoEditStrategy();

        // All tests just operate on the "edithandling" document; the contents are
        // ignored and replaced with the before-document passed in
        IFile file = getLayoutFile(getProject(), "edithandling.xml");

        IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
        assertNotNull(page);
        IEditorPart editor = IDE.openEditor(page, file);
        assertTrue(editor instanceof AndroidXmlEditor);
        AndroidXmlEditor layoutEditor = (AndroidXmlEditor) editor;
        ISourceViewer viewer = layoutEditor.getStructuredSourceViewer();

        String newDocumentContent = stripCaret(before);
        IDocument document = viewer.getDocument();
        document.replace(0, document.getLength(), newDocumentContent);

        // Determine the offset, and possibly make text range selections as well
        int offset = updateCaret(viewer, before);

        DocumentCommand c = new TestDocumentCommand();
        c.doit = true;
        c.offset = offset;
        c.caretOffset = -1;
        c.length = 0;
        c.text = "\n";

        s.customizeDocumentCommand(document, c);

        if (c.doit) {
            if (c.length == 0 && c.text == null) {
                return;
            }

            document.replace(c.offset, c.length, c.text);

            int caretOffset = c.offset + c.text.length();

            // The shiftsCaret flag doesn't behave the way it's documented to
            //if (c.shiftsCaret && c.caretOffset != -1) {
            if (c.caretOffset != -1) {
                caretOffset = c.caretOffset;
            }
            viewer.setSelectedRange(caretOffset, 0);
        }

        String text = document.get();
        Point selectedRange = viewer.getSelectedRange();
        assert selectedRange.y == 0;
        String textWithCaret = text;
        if (selectedRange.x >= 0) {
            textWithCaret = text.substring(0, selectedRange.x) + "^" +
                text.substring(selectedRange.x);
        }

        assertEquals(after, textWithCaret);
    }

    private static String stripCaret(String s) {
        int index = s.indexOf('^');
        assertTrue(index != -1);
        return s.substring(0, index) + s.substring(index + 1);
    }

    public void testCornerCase1() throws Exception {
        checkInsertNewline("^", "\n^");
    }

    public void testCornerCase2() throws Exception {
        checkInsertNewline(
                "\n^",

                "\n\n^");
    }

    public void testCornerCase3() throws Exception {
        checkInsertNewline(
                "    ^",

                "    \n" +
                "    ^");
    }

    public void testSimpleIndentation1() throws Exception {
        checkInsertNewline(
                "   ^ ",

                "   \n" +
                "   ^ ");
    }

    public void testSimpleIndentation2() throws Exception {
        checkInsertNewline(
                "\n" +
                "   foo^\n",

                "\n" +
                "   foo\n" +
                "   ^\n");
    }

    public void testSimpleIndentation3() throws Exception {
        checkInsertNewline(
                "\n" +
                "    <newtag>^\n",

                "\n" +
                "    <newtag>\n" +
                "        ^\n");
    }

    public void testSimpleIndentation4() throws Exception {
        checkInsertNewline(
                "\n" +
                "    <newtag/>^\n",

                "\n" +
                "    <newtag/>\n" +
                "    ^\n");
    }

    public void testSimpleIndentation5() throws Exception {
        checkInsertNewline(
                "\n" +
                "    <newtag^\n",
                "\n" +
                "    <newtag\n" +
                "        ^\n");
    }

    public void testSplitAttribute() throws Exception {
        checkInsertNewline(
                "\n" +
                "    <newtag ^attribute='value'/>\n",

                "\n" +
                "    <newtag \n" +
                "        ^attribute='value'/>\n");
    }

    public void testIndentationInComments1() throws Exception {
        // Make sure that inside a comment we ignore tags etc
        checkInsertNewline(
                "<!--\n   foo^\n--->\n",

                "<!--\n   foo\n   ^\n--->\n");
    }

    public void testIndentationInComments2() throws Exception {
        // Make sure that inside a comment we ignore tags etc
        checkInsertNewline(
                "\n" +
                "<!--\n" +
                "<foo><^\n" +
                "-->\n",

                "\n" +
                "<!--\n" +
                "<foo><\n" +
                "^\n" +
                "-->\n");
    }

    public void testSurroundCaret() throws Exception {
        checkInsertNewline(
                "\n" +
                "    <item>^</item>\n",

                "\n" +
                "    <item>\n" +
                "        ^\n" +
                "    </item>\n");
    }

    public void testSurroundCaret2() throws Exception {
        // This test combines both surround with and continuing earlier lines (where
        // it searches for a matching tag)
        checkInsertNewline(
                "\n" +
                "    <foo\n" +
                "        name='value'>^</foo>\n",

                "\n" +
                "    <foo\n" +
                "        name='value'>\n" +
                "        ^\n" +
                "    </foo>\n");
    }

    public void testContinueEarlierLine1() throws Exception {
        // Here we need to indent to the exact location of an earlier line
        checkInsertNewline(
                "\n" +
                "    <foo\n" +
                "        name='value'/>^\n",

                "\n" +
                "    <foo\n" +
                "        name='value'/>\n" +
                "    ^\n");
    }

    public void testContinueEarlierLine2() throws Exception {
        checkInsertNewline(
                "\n" +
                "    <foo\n" +
                "        name='value'></foo>^\n",

                "\n" +
                "    <foo\n" +
                "        name='value'></foo>\n" +
                "    ^\n");
        // Note that
        //    <foo
        //           >
        //    </foo>
        // does not require special handling, this only happens with the closing tag is sharing
        // a line.
    }

    public void testContinueEarlierLine3() throws Exception {
        // Make sure the code to look up the corresponding opening tag works properly
        checkInsertNewline(
                "\n" +
                "    <foo\n" +
                "        name='value'><bar></bar><baz/></foo>^\n",

                "\n" +
                "    <foo\n" +
                "        name='value'><bar></bar><baz/></foo>\n" +
                "    ^\n");
    }

    public void testContinueEarlierLine4() throws Exception {
        checkInsertNewline(
                "    <Button\n" +
                "        android:id=\"@+id/button1\"\n" +
                "        android:layout_width=\"wrap_content\"\n" +
                "        android:layout_height=\"wrap_content\"\n" +
                "        android:text=\"Button\" >^\n" +
                "    </Button>\n",

                "    <Button\n" +
                "        android:id=\"@+id/button1\"\n" +
                "        android:layout_width=\"wrap_content\"\n" +
                "        android:layout_height=\"wrap_content\"\n" +
                "        android:text=\"Button\" >\n" +
                "        ^\n" +
                "    </Button>\n");
    }

    public void testIndent() throws Exception {
        checkInsertNewline(
                "    <Button\n" +
                "        attr=\"value\"></Button>^\n",

                "    <Button\n" +
                "        attr=\"value\"></Button>\n" +
                "    ^\n" +
                "");
    }

    public void testLineBeginning1() throws Exception {
        // Test that if you insert on a blank line, we just add a newline and indent
        checkInsertNewline(
                "<foo>\n" +
                "^\n" +
                "</foo>",

                "<foo>\n" +
                "\n" +
                "    ^\n" +
                "</foo>");
    }

    public void testLineBeginning2() throws Exception {
        // Test that if you insert with the caret on the beginning of a line that has
        // content, we insert an indent correctly
        checkInsertNewline(
                "<foo>\n" +
                "^    <bar/>\n" +
                "</foo>",

                "<foo>\n" +
                "\n" +
                "    ^<bar/>\n" +
                "</foo>");
    }

    public void testLineBeginning3() throws Exception {
        checkInsertNewline(
                "<foo>\n" +
                "    <bar>\n" +
                "^\n" +
                "        <baz/>\n" +
                "    </bar>\n" +
                "</foo>",

                "<foo>\n" +
                "    <bar>\n" +
                "\n" +
                "        ^\n" +
                "        <baz/>\n" +
                "    </bar>\n" +
                "</foo>");

    }

    public void testLineBeginning4() throws Exception {
        // Test that if you insert with the caret on the beginning of a line that has
        // content, we insert an indent correctly
        checkInsertNewline(
                "<foo>\n" +
                "    <bar>\n" +
                "\n" +
                "^        <baz/>\n" +
                "    </bar>\n" +
                "</foo>",

                "<foo>\n" +
                "    <bar>\n" +
                "\n" +
                "\n" +
                "        ^<baz/>\n" +
                "    </bar>\n" +
                "</foo>");
    }

    public void testLineBeginning5() throws Exception {
        // Test that if you insert with the caret on the beginning of a line that has
        // content, we insert an indent correctly
        checkInsertNewline(
                "<foo>\n" +
                "    <bar>\n" +
                "\n" +
                "    ^    <baz/>\n" +
                "    </bar>\n" +
                "</foo>",

                "<foo>\n" +
                "    <bar>\n" +
                "\n" +
                "    \n" +
                "        ^<baz/>\n" +
                "    </bar>\n" +
                "</foo>");
    }

    public void testLineBeginning6() throws Exception {

        checkInsertNewline(
                "    <foo>\n" +
                "        <bar>\n" +
                "            \n" +
                "        \n" +
                "^        </bar>\n" +
                "    </foo>\n",

                "    <foo>\n" +
                "        <bar>\n" +
                "            \n" +
                "        \n" +
                "\n" +
                "        ^</bar>\n" +
                "    </foo>\n");
    }

    public void testBlankContinuation() throws Exception {

        checkInsertNewline(
                "    <foo>\n" +
                "        <bar>\n" +
                "            ^\n" +
                "        </bar>\n" +
                "    </foo>\n" +
                "",

                "    <foo>\n" +
                "        <bar>\n" +
                "            \n" +
                "            ^\n" +
                "        </bar>\n" +
                "    </foo>\n" +
                "");
    }

    /**
     * To test
     *    When you press / after < I should reindent the current line. For example,
     *    if you have
     *        <foo>
     *            <bar>
     *            </ the moment you've typed this we should dedent it back out
     *    When you press newline we need to reindent
     */

    /** Subclassed for test usage since constructor is protected */
    private class TestDocumentCommand extends DocumentCommand {
        public TestDocumentCommand() {
        }
    }
}
