package io.github.qeesung.adapter;

import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.psi.tree.IElementType;

import java.util.HashSet;
import java.util.Set;
import java.util.Stack;

import static com.intellij.codeInsight.highlighting.BraceMatchingUtil.*;
import static io.github.qeesung.brace.BraceTokenTypes.*;

/**
 * Brace matching util adapter.
 */
public class BraceMatchingUtilAdapter {

    public static final Set<String> STRING_TOKEN_SET = new HashSet<>();

    static {
        STRING_TOKEN_SET.add(GROOVY_STRING_TOKEN);
        STRING_TOKEN_SET.add(GROOVY_SINGLE_QUOTE_TOKEN);
        STRING_TOKEN_SET.add(KOTLIN_STRING_TOKEN);
        STRING_TOKEN_SET.add(KOTLIN_CHAR_TOKEN);
        STRING_TOKEN_SET.add(JS_STRING_TOKEN);
        STRING_TOKEN_SET.add(JAVA_STRING_TOKEN);
        STRING_TOKEN_SET.add(SCALA_STRING_TOKEN);
        STRING_TOKEN_SET.add(HASKELL_STRING_TOKEN);
    }

    /**
     * check is the current token type is string token.
     * @param tokenType token type
     * @return is string token
     */
    public static boolean isStringToken(IElementType tokenType) {
        String elementName = tokenType.toString();
        return STRING_TOKEN_SET.contains(elementName);
    }

    /**
     * Find the left closest brace offset position.
     *
     * @param iterator highlighter iterator
     * @param lparenTokenType left token type to be paired
     * @param fileText file text
     * @param fileType file type
     * @return offset
     */
    public static int findLeftLParen(HighlighterIterator iterator,
                                     IElementType lparenTokenType,
                                     CharSequence fileText,
                                     FileType fileType, boolean isBlockCaret) {
        int lastLbraceOffset = -1;
        int initOffset = iterator.atEnd() ? -1 : iterator.getStart();
        Stack<IElementType> braceStack = new Stack<>();
        for (; !iterator.atEnd(); iterator.retreat()) {
            final IElementType tokenType = iterator.getTokenType();

            if (isLBraceToken(iterator, fileText, fileType)) {
                if (!isBlockCaret && initOffset == iterator.getStart())
                    continue;
                if (!braceStack.isEmpty()) {
                    IElementType topToken = braceStack.pop();
                    if (!isPairBraces(tokenType, topToken, fileType)) {
                        break; // unmatched braces
                    }
                } else {
                    if (tokenType == lparenTokenType) {
                        return iterator.getStart();
                    } else {
                        break;
                    }
                }
            } else if (isRBraceToken(iterator, fileText, fileType)) {
                if (initOffset == iterator.getStart())
                    continue;
                braceStack.push(iterator.getTokenType());
            }
        }

        return lastLbraceOffset;
    }

    /**
     * find the right closest brace offset position
     *
     * @param iterator highlight iterator
     * @param rparenTokenType right token type to paired
     * @param fileText file text
     * @param fileType file type
     * @return offset
     */
    public static int findRightRParen(HighlighterIterator iterator,
                                      IElementType rparenTokenType,
                                      CharSequence fileText,
                                      FileType fileType, boolean isBlockCaret) {
        int lastRbraceOffset = -1;
        int initOffset = iterator.atEnd() ? -1 : iterator.getStart();
        Stack<IElementType> braceStack = new Stack<>();
        for (; !iterator.atEnd(); iterator.advance()) {
            final IElementType tokenType = iterator.getTokenType();

            if (isRBraceToken(iterator, fileText, fileType)) {
                if (!braceStack.isEmpty()) {
                    IElementType topToken = braceStack.pop();
                    if (!isPairBraces(tokenType, topToken, fileType)) {
                        break; // unmatched braces
                    }
                } else {
                    if (tokenType == rparenTokenType) {
                        return iterator.getStart();
                    } else {
                        break;
                    }
                }
            } else if (isLBraceToken(iterator, fileText, fileType)) {
                if (isBlockCaret && initOffset == iterator.getStart())
                    continue;
                else
                    braceStack.push(iterator.getTokenType());
            }
        }

        return lastRbraceOffset;
    }
}