/*******************************************************************************
 * Copyright (c) 2006, 2018 Wind River Systems, Inc. and others.
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Anton Leherbauer (Wind River Systems) - initial API and implementation
 *     Sergey Prigogin (Google)
 *     Andrew Ferguson (Symbian)
 *     Andrew Gvozdev
 *******************************************************************************/
package org.eclipse.linuxtools.systemtap.ui.ide.test.editors.stp;

import static org.junit.Assert.fail;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentCommand;
import org.eclipse.jface.text.IAutoEditStrategy;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.TextUtilities;

/**
 * IAutoEditStrategy related tests
 */
public class AbstractAutoEditTest {

    private String name;

    protected AbstractAutoEditTest(String name) {
        super();
        this.name = name;
    }

    public String getName() {
        return name;
    }

    /**
     * Helper class to test the auto-edit strategies on a document.
     * Split out from CAutoIndentTest.
     */
    protected static class AutoEditTester {
        private IAutoEditStrategy tabsToSpacesConverter;
        private Map<String, IAutoEditStrategy> fStrategyMap = new HashMap<>();
        IDocument fDoc;
        private String fPartitioning;
        private int fCaretOffset;

        public AutoEditTester(IDocument doc, String partitioning) {
            super();
            fDoc = doc;
            fPartitioning = partitioning;
        }

        public void setTabsToSpacesConverter(IAutoEditStrategy converter) {
            tabsToSpacesConverter = converter;
        }

        public void setAutoEditStrategy(String contentType, IAutoEditStrategy aes) {
            fStrategyMap.put(contentType, aes);
        }

        public IAutoEditStrategy getAutoEditStrategy(String contentType) {
            return fStrategyMap.get(contentType);
        }

        /**
         * Empties the document, and returns the caret to the origin (0,0)
         * @return <code>this</code> for method chaining
         */
        public AutoEditTester reset() {
            try {
                goTo(0,0);
                fDoc.set("");
            } catch(BadLocationException ble) {
                fail(ble.getMessage());
            }
            return this;
        }

        public void type(String text) throws BadLocationException {
            for (int i = 0; i < text.length(); ++i) {
                type(text.charAt(i));
            }
        }

        public void type(char c) throws BadLocationException {
            TestDocumentCommand command = new TestDocumentCommand(fCaretOffset, 0, new String(new char[] { c }));
            customizeDocumentCommand(command);
            fCaretOffset = command.exec(fDoc);
        }

        private void customizeDocumentCommand(TestDocumentCommand command) throws BadLocationException {
            if (tabsToSpacesConverter != null) {
                tabsToSpacesConverter.customizeDocumentCommand(fDoc, command);
            }
            IAutoEditStrategy aes = getAutoEditStrategy(getContentType(command.offset));
            if (aes != null) {
                aes.customizeDocumentCommand(fDoc, command);
            }
        }

        public void type(int offset, String text) throws BadLocationException {
            fCaretOffset = offset;
            type(text);
        }

        public void type(int offset, char c) throws BadLocationException {
            fCaretOffset = offset;
            type(c);
        }

        public void paste(String text) throws BadLocationException {
            TestDocumentCommand command = new TestDocumentCommand(fCaretOffset, 0, text);
            customizeDocumentCommand(command);
            fCaretOffset = command.exec(fDoc);
        }

        public void paste(int offset, String text) throws BadLocationException {
            fCaretOffset = offset;
            paste(text);
        }

        public void backspace(int n) throws BadLocationException {
            for (int i = 0; i < n; ++i) {
                backspace();
            }
        }

        public void backspace() throws BadLocationException {
            TestDocumentCommand command = new TestDocumentCommand(fCaretOffset - 1, 1, ""); //$NON-NLS-1$
            customizeDocumentCommand(command);
            fCaretOffset = command.exec(fDoc);
        }

        public int getCaretOffset() {
            return fCaretOffset;
        }

        public int setCaretOffset(int offset) {
            fCaretOffset = offset;
            if (fCaretOffset < 0)
                fCaretOffset = 0;
            else if (fCaretOffset > fDoc.getLength())
                fCaretOffset = fDoc.getLength();
            return fCaretOffset;
        }

        /**
         * Moves caret right or left by the given number of characters.
         *
         * @param shift Move distance.
         * @return New caret offset.
         */
        public int moveCaret(int shift) {
            return setCaretOffset(fCaretOffset + shift);
        }

        public int goTo(int line) throws BadLocationException {
            fCaretOffset = fDoc.getLineOffset(line);
            return fCaretOffset;
        }

        public int goTo(int line, int column) throws BadLocationException {
            if (column < 0 || column > fDoc.getLineLength(line)) {
                throw new BadLocationException("No column " + column + " in line " + line); //$NON-NLS-1$ $NON-NLS-2$
            }
            fCaretOffset = fDoc.getLineOffset(line) + column;
            return fCaretOffset;
        }

        public int getCaretLine() throws BadLocationException {
            return fDoc.getLineOfOffset(fCaretOffset);
        }

        public int getCaretColumn() throws BadLocationException {
            IRegion region = fDoc.getLineInformationOfOffset(fCaretOffset);
            return fCaretOffset - region.getOffset();
        }

        public char getChar() throws BadLocationException {
            return getChar(0);
        }

        public char getChar(int i) throws BadLocationException {
            return fDoc.getChar(fCaretOffset+i);
        }

        public String getLine() throws BadLocationException {
            return getLine(0);
        }

        public String getLine(int i) throws BadLocationException {
            IRegion region = fDoc.getLineInformation(getCaretLine() + i);
            return fDoc.get(region.getOffset(), region.getLength());
        }

        public String getContentType(int offset) throws BadLocationException {
            return TextUtilities.getContentType(fDoc, fPartitioning, offset, false); //TRUE??
        }
    }

    /**
     * A DocumentCommand with public constructor and exec method.
     */
    protected static class TestDocumentCommand extends DocumentCommand {

        public TestDocumentCommand(int offset, int length, String text) {
            super();
            doit = true;
            this.text = text;

            this.offset = offset;
            this.length = length;

            owner = null;
            caretOffset = -1;
        }

        /**
         * @param doc
         * @return the new caret position.
         * @throws BadLocationException
         */
        public int exec(IDocument doc) throws BadLocationException {
            doc.replace(offset, length, text);
            return caretOffset != -1 ?
                        caretOffset :
                        offset + (text == null ? 0 : text.length());
        }
    }
}
