package me.qwq.doghouse.flexmark.ext.fancyquote;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.parser.block.AbstractBlockParser;
import com.vladsch.flexmark.parser.block.AbstractBlockParserFactory;
import com.vladsch.flexmark.parser.block.BlockContinue;
import com.vladsch.flexmark.parser.block.BlockParser;
import com.vladsch.flexmark.parser.block.BlockParserFactory;
import com.vladsch.flexmark.parser.block.BlockStart;
import com.vladsch.flexmark.parser.block.CustomBlockParserFactory;
import com.vladsch.flexmark.parser.block.MatchedBlockParser;
import com.vladsch.flexmark.parser.block.ParserState;
import com.vladsch.flexmark.parser.core.BlockQuoteParser;
import com.vladsch.flexmark.parser.core.FencedCodeBlockParser;
import com.vladsch.flexmark.parser.core.HeadingParser;
import com.vladsch.flexmark.parser.core.HtmlBlockParser;
import com.vladsch.flexmark.parser.core.IndentedCodeBlockParser;
import com.vladsch.flexmark.parser.core.ListBlockParser;
import com.vladsch.flexmark.parser.core.ThematicBreakParser;
import com.vladsch.flexmark.util.ast.Block;
import com.vladsch.flexmark.util.data.DataHolder;
import com.vladsch.flexmark.util.sequence.BasedSequence;

public class FancyQuoteParser extends AbstractBlockParser {

    // 参照的 FencedCodeBlock
    final private static Pattern OPENING_FENCE = Pattern.compile("^\\:{3,}(?!.*\\:)");
    final private static Pattern CLOSING_FENCE = Pattern.compile("^(?:\\:{3,})(?=[ \t]*$)");

    final private FancyQuote block = new FancyQuote();
    final private char fenceChar;
    final private int fenceLength;
    final private int fenceIndent;
    final private int fenceMarkerIndent;
    final private boolean matchingCloser;
    final private List<BasedSequence> lineSegments = BasedSequence.EMPTY_LIST;

    public FancyQuoteParser(DataHolder options, char fenceChar, int fenceLength, int fenceIndent, int fenceMarkerIndent) {
        this.fenceChar = fenceChar;
        this.fenceLength = fenceLength;
        this.fenceIndent = fenceIndent;
        this.fenceMarkerIndent = fenceIndent + fenceMarkerIndent;
        this.matchingCloser = Parser.MATCH_CLOSING_FENCE_CHARACTERS.get(options);
    }
    
    @Override
    public boolean isContainer() {
        return true;
    }

    @Override
    public boolean canContain(ParserState state, BlockParser blockParser, Block block) {
        return true;
    }
    
    @Override
    public boolean canInterruptBy(BlockParserFactory factory) {
        if (factory instanceof BlockFactory) {
            return false;
        }
        return true;
    }
    
    @Override
    public Block getBlock() {
        return block;
    }

    public int getFenceIndent() {
        return fenceIndent;
    }

    public int getFenceMarkerIndent() {
        return fenceMarkerIndent;
    }

    @Override
    public BlockContinue tryContinue(ParserState state) {
        
        // 不知道为什么 finished 了还进来，那就加一个 none 吧
        if (isClosed()) return BlockContinue.none();
        
        int nextNonSpace = state.getNextNonSpaceIndex();
        //int newIndex = state.getIndex();
        BasedSequence line = state.getLine();
        
        Matcher matcher;
        boolean matches = (state.getIndent() <= 3 &&
                nextNonSpace < line.length() &&
                (!matchingCloser || line.charAt(nextNonSpace) == fenceChar));

        if (matches) {
            BasedSequence trySequence = line.subSequence(nextNonSpace, line.length());
            matcher = CLOSING_FENCE.matcher(trySequence);
            if (matcher.find()) {
                int foundFenceLength = matcher.group(0).length();

                if (foundFenceLength >= fenceLength) {
                    // closing fence - we're at end of line, so we can finalize now
                    block.setClosingMarker(trySequence.subSequence(0, foundFenceLength));
                    //finalizeClosedBlock();
                    return BlockContinue.finished();
                }
            }
        }
        
        int newColumn = state.getColumn() + state.getIndent();
        
        /*if (state.isBlankLine()) {
            return BlockContinue.atIndex(newIndex);
        }*/
        if (state.isBlank()) {
            newColumn += nextNonSpace + 1;
        }
        lineSegments.add(line);
        return BlockContinue.atColumn(newColumn);
        
        
        // skip optional spaces of fence indent
        /*int i = fenceIndent;
        while (i > 0 && newIndex < line.length() && line.charAt(newIndex) == ' ') {
            newIndex++;
            i--;
        }
        return BlockContinue.atIndex(newIndex);*/
    }

    @Override
    public boolean isPropagatingLastBlankLine(BlockParser lastMatchedBlockParser) {
        return false;
    }

    @Override
    public void closeBlock(ParserState state) {
        block.setContentLines(lineSegments);
        block.setCharsFromContent();

        if (!Parser.BLANK_LINES_IN_AST.get(state.getProperties())) {
            removeBlankLines();
        }
    }

    public static class Factory implements CustomBlockParserFactory {
        @Nullable
        @Override
        public Set<Class<?>> getAfterDependents() {
            return Collections.emptySet();
        }

        @Nullable
        @Override
        public Set<Class<?>> getBeforeDependents() {
            return new HashSet<>(Arrays.asList(
                    BlockQuoteParser.Factory.class,
                    HeadingParser.Factory.class,
                    //FancyQuoteParser.Factory.class,
                    FencedCodeBlockParser.Factory.class,
                    HtmlBlockParser.Factory.class,
                    ThematicBreakParser.Factory.class,
                    ListBlockParser.Factory.class,
                    IndentedCodeBlockParser.Factory.class
            ));
        }
        
        @Override
        public boolean affectsGlobalScope() {
            return false;
        }

        @NotNull
        @Override
        public BlockParserFactory apply(@NotNull DataHolder options) {
            return new BlockFactory(options);
        }
    }

    private static class BlockFactory extends AbstractBlockParserFactory {
        private BlockFactory(DataHolder options) {
            super(options);
        }

        @Override
        public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
            int nextNonSpace = state.getNextNonSpaceIndex();
            BasedSequence line = state.getLine();
            Matcher matcher;
            if (state.getIndent() < 4) {
                BasedSequence trySequence = line.subSequence(nextNonSpace, line.length());
                if ((matcher = OPENING_FENCE.matcher(trySequence)).find()) {
                    /*
                     * 除了进入匹配机制像 FencedCodeBlockParser 以外，其他都像 BlockQuoteParser。
                     * 不知道为什么在 tryStart 方法和 tryContinue 方法执行中/后没有像 BlockQuote
                     * 一样进入到 addLine 方法，导致 content.getLines() 为空行，进一步导致原本参
                     * 照 FencedCodeBlockParser 的 closeBlock 方法没办法根据 content.getLines()
                     * 获取到处于首行的 info 信息。所以只能在此处理。
                     * 
                     * 如果成功匹配正则，则 trySequence 应该是像 :::mess-info 一样的序列，那么
                     * info 的位置就是除去 fenceLength（此处为 3）后的子串。匹配完毕后置于 block
                     * 之中。
                     */
                    int fenceLength = matcher.group(0).length();
                    char fenceChar = matcher.group(0).charAt(0);
                    BasedSequence clazz = trySequence.subSequence(fenceLength);
                    FancyQuoteParser blockParser = new FancyQuoteParser(state.getProperties(), fenceChar, fenceLength, state.getIndent(), nextNonSpace);
                    blockParser.block.setOpeningMarker(trySequence.subSequence(0, fenceLength));
                    blockParser.block.setInfo(clazz);
                    return BlockStart.of(blockParser).atIndex(nextNonSpace + trySequence.length());
                }
            }
            return BlockStart.none();
        }
    }
}


