/*
 * Copyright (C) 2016 Reece H. Dunn
 *
 * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
 *
 * 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 uk.co.reecedunn.intellij.plugin.xquery.tests.parser;

import com.intellij.lang.ASTNode;
import com.intellij.lang.LanguageASTFactory;
import com.intellij.lang.ParserDefinition;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.impl.PsiBuilderImpl;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.CompositeElement;
import com.intellij.psi.impl.source.tree.FileElement;
import com.intellij.psi.impl.source.tree.LeafElement;
import com.intellij.psi.tree.TokenSet;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.testFramework.ParsingTestCase;
import org.apache.xmlbeans.impl.common.IOUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import uk.co.reecedunn.intellij.plugin.xquery.ast.xquery.XQueryFile;
import uk.co.reecedunn.intellij.plugin.xquery.lang.XQuery;
import uk.co.reecedunn.intellij.plugin.xquery.parser.XQueryASTFactory;
import uk.co.reecedunn.intellij.plugin.xquery.parser.XQueryParserDefinition;
import uk.co.reecedunn.intellij.plugin.xquery.settings.XQueryProjectSettings;

import java.io.*;

@SuppressWarnings("SameParameterValue")
public abstract class ParserTestCase extends ParsingTestCase {
    public ParserTestCase() {
        super("", ".xqy", new XQueryParserDefinition());
    }

    protected void setUp() throws Exception {
        super.setUp();
        registerApplicationService(XQueryProjectSettings.class, new XQueryProjectSettings());
        addExplicitExtension(LanguageASTFactory.INSTANCE, XQuery.INSTANCE, new XQueryASTFactory());
    }

    // region Parser Test Helpers

    void initializeSettings(XQueryProjectSettings settings) {
    }

    protected XQueryProjectSettings getSettings() {
        return XQueryProjectSettings.getInstance(myProject);
    }

    String loadResource(String resource) {
        ClassLoader loader = ParserTestCase.class.getClassLoader();
        InputStream stream = loader.getResourceAsStream(resource);
        StringWriter writer = new StringWriter();
        try {
            IOUtil.copyCompletely(new InputStreamReader(stream), writer);
        } catch (Exception e) {
            //
        }
        return writer.toString();
    }

    LightVirtualFile createVirtualFile(@NonNls String name, String text) {
        LightVirtualFile file = new LightVirtualFile(name, myLanguage, text);
        file.setCharset(CharsetToolkit.UTF8_CHARSET);
        return file;
    }

    FileViewProvider getFileViewProvider(@NotNull Project project, LightVirtualFile file, boolean physical) {
        final PsiManager manager = PsiManager.getInstance(project);
        final FileViewProviderFactory factory = LanguageFileViewProviders.INSTANCE.forLanguage(XQuery.INSTANCE);
        FileViewProvider viewProvider = factory != null ? factory.createFileViewProvider(file, XQuery.INSTANCE, manager, physical) : null;
        if (viewProvider == null) viewProvider = new SingleRootFileViewProvider(manager, file, physical);
        return viewProvider;
    }

    private void buildPsi(@NotNull ParserDefinition parserDefinition, ASTNode node, LightVirtualFile file) {
        if (node instanceof CompositeElement) {
            CompositeElement element = (CompositeElement)node;
            if (node instanceof FileElement) {
                element.setPsi(createFile(file));
            } else if (!(node instanceof PsiErrorElement)) {
                element.setPsi(parserDefinition.createElement(node));
            }
        }

        for (ASTNode child : node.getChildren(null)) {
            buildPsi(parserDefinition, child, file);
        }
    }

    private void prettyPrintASTNode(@NotNull StringBuilder prettyPrinted, ASTNode node, int depth) {
        for (int i = 0; i != depth; ++i) {
            prettyPrinted.append("   ");
        }

        if (node instanceof FileElement) {
            prettyPrinted.append("FileElement");
        } else {
            String[] names = node.getPsi().getClass().getName().split("\\.");
            prettyPrinted.append(names[names.length - 1].replace("PsiImpl", "Impl"));
        }

        prettyPrinted.append('[');
        prettyPrinted.append(node.getElementType());
        prettyPrinted.append('(');
        prettyPrinted.append(node.getTextRange().getStartOffset());
        prettyPrinted.append(':');
        prettyPrinted.append(node.getTextRange().getEndOffset());
        prettyPrinted.append(')');
        prettyPrinted.append(']');
        if ((node instanceof LeafElement) || (node instanceof PsiErrorElement)) {
            prettyPrinted.append('(');
            prettyPrinted.append('\'');
            if (node instanceof PsiErrorElement) {
                PsiErrorElement error = (PsiErrorElement)node;
                prettyPrinted.append(error.getErrorDescription());
            } else {
                prettyPrinted.append(node.getText().replace("\n", "\\n"));
            }
            prettyPrinted.append('\'');
            prettyPrinted.append(')');
        }
        prettyPrinted.append('\n');

        for (ASTNode child : node.getChildren(null)) {
            prettyPrintASTNode(prettyPrinted, child, depth + 1);
        }
    }

    String prettyPrintASTNode(XQueryFile file) {
        StringBuilder prettyPrinted = new StringBuilder();
        prettyPrintASTNode(prettyPrinted, file.getNode(), 0);
        return prettyPrinted.toString();
    }

    @NotNull
    private XQueryFile parseFile(@NotNull LightVirtualFile file) {
        initializeSettings(getSettings());

        ParserDefinition parserDefinition = new XQueryParserDefinition();
        TokenSet whitespaces = parserDefinition.getWhitespaceTokens();
        TokenSet comments = parserDefinition.getCommentTokens();
        Lexer lexer = parserDefinition.createLexer(myProject);

        PsiBuilder builder = new PsiBuilderImpl(myProject, null, whitespaces, comments, lexer, null, file.getContent(), null, null);
        ASTNode node = parserDefinition.createParser(myProject).parse(parserDefinition.getFileNodeType(), builder);
        buildPsi(parserDefinition, node, file);
        return (XQueryFile)node.getPsi();
    }

    @NotNull
    protected XQueryFile parseText(@NotNull String text) {
        return parseFile(createVirtualFile("testcase.xqy", text));
    }

    @NotNull
    protected XQueryFile parseResource(String resource) {
        return parseFile(createVirtualFile(resource, loadResource(resource)));
    }

    // endregion
}

@@ -735,167 +735,8 @@
 ry;%0A
-import com.intellij.lang.ParserDefinition;%0Aimport com.intellij.lang.PsiBuilder;%0Aimport com.intellij.lang.impl.PsiBuilderImpl;%0Aimport com.intellij.lexer.Lexer;%0A
 impo
@@ -776,16 +776,16 @@
 roject;%0A
+
 import c
@@ -855,67 +855,8 @@
 .*;%0A
-import com.intellij.psi.impl.source.tree.CompositeElement;%0A
 impo
@@ -963,47 +963,8 @@
 nt;%0A
-import com.intellij.psi.tree.TokenSet;%0A
 impo
@@ -3482,606 +3482,8 @@
  %7D%0A%0A
-    private void buildPsi(@NotNull ParserDefinition parserDefinition, ASTNode node, LightVirtualFile file) %7B%0A        if (node instanceof CompositeElement) %7B%0A            CompositeElement element = (CompositeElement)node;%0A            if (node instanceof FileElement) %7B%0A                element.setPsi(createFile(file));%0A            %7D else if (!(node instanceof PsiErrorElement)) %7B%0A                element.setPsi(parserDefinition.createElement(node));%0A            %7D%0A        %7D%0A%0A        for (ASTNode child : node.getChildren(null)) %7B%0A            buildPsi(parserDefinition, child, file);%0A        %7D%0A    %7D%0A%0A
     
@@ -5376,25 +5376,24 @@
 ngs());%0A
-%0A
         
 ParserDe
@@ -5388,610 +5388,48 @@
     
-ParserDefinition parserDefinition = new XQueryParserDefinition();%0A        TokenSet whitespaces = parserDefinition.getWhitespaceTokens();%0A        TokenSet comments = parserDefinition.getCommentTokens();%0A        Lexer lexer = parserDefinition.createLexer(myProject);%0A%0A        PsiBuilder builder = new PsiBuilderImpl(myProject, null, whitespaces, comments, lexer, null, file.getContent(), null, null);%0A        ASTNode node = parserDefinition.createParser(myProject).parse(parserDefinition.getFileNodeType(), builder);%0A        buildPsi(parserDefinition, node, file);%0A        return (XQueryFile)node.getPsi(
+return (XQueryFile)super.createFile(file
 );%0A 

Simplify the ParserTestCase::parseFile implementation.