package com.github.richardjwild.randomizer.types.pattern.parserstate;

import com.github.richardjwild.randomizer.types.pattern.StringPatternBuilder;
import com.github.richardjwild.randomizer.types.pattern.StringPatternParser;
import com.github.richardjwild.randomizer.types.pattern.StringPatternParserException;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import static com.github.richardjwild.randomizer.localization.Messages.*;
import static com.github.richardjwild.randomizer.streams.Characters.from;
import static com.github.richardjwild.randomizer.types.pattern.StringPatternParser.exception;

public class DefineCharacterRangeState extends ParserState {

    private final List<Character> permittedCharacters = new ArrayList<>();

    public DefineCharacterRangeState(StringPatternParser parser, StringPatternBuilder builder) {
        super(parser, builder);
    }

    @Override
    public ParserState handleCharacter(char c) {
        switch (c) {
            case '\\':
                treatNextCharacterAsLiteral();
                return this;
            case ']':
                if (nextCharacterDefinesLength()) {
                    parser.skip(1);
                    return new DefineRangeLengthState(parser, builder, permittedCharacters);
                } else
                    throw new StringPatternParserException(UNEXPECTED_CHARACTER_WANTED_OPENCURLYBRACE);
            default:
                addPermittedCharacterOrDefineRange(c);
                return this;
        }
    }

    private void treatNextCharacterAsLiteral() {
        char nextChar = parser.lookAhead(1).orElseThrow(exception(UNEXPECTED_PATTERN_END_WANTED_CHARACTER));
        parser.skip(1);
        addPermittedCharacterOrDefineRange(nextChar);
    }

    private void addPermittedCharacterOrDefineRange(char nextChar) {
        if (nextCharacterDefinesRange())
            addRangeOfPermittedCharacters(nextChar);
        else
            permittedCharacters.add(nextChar);
    }

    private boolean nextCharacterDefinesRange() {
        return '-' == parser.lookAhead(1)
                .orElseThrow(exception(UNEXPECTED_PATTERN_END_WANTED_CHARACTER_DASH_CLOSESQUAREBRACE));
    }

    private void addRangeOfPermittedCharacters(char minChar) {
        char maxChar = getMaxChar();
        allCharsBetween(minChar, maxChar).forEach(permittedCharacters::add);
    }

    private char getMaxChar() {
        char maxChar = parser.lookAhead(2).orElseThrow(exception(UNEXPECTED_PATTERN_END_WANTED_CHARACTER));
        parser.skip(2);
        if (maxChar == '\\') {
            maxChar = parser.lookAhead(1).orElseThrow(exception(UNEXPECTED_PATTERN_END_WANTED_CHARACTER));
            parser.skip(1);
        }
        return maxChar;
    }

    private Stream<Character> allCharsBetween(char minChar, char maxChar) {
        if (minChar <= maxChar)
            return from(minChar).limit(maxChar - minChar);
        else
            return from(maxChar).limit(minChar - maxChar);
    }

    private boolean nextCharacterDefinesLength() {
        return '{' == parser.lookAhead(1).orElseThrow(exception(UNEXPECTED_PATTERN_END_WANTED_OPENCURLYBRACE));
    }

    @Override
    public void patternEnded() {
        throw new StringPatternParserException(UNEXPECTED_PATTERN_END_WANTED_CLOSESQUAREBRACE);
    }
}

@@ -1124,32 +1124,33 @@
             if (
+!
 nextCharacterDef
@@ -1162,23 +1162,121 @@
 ength())
- %7B
 %0A    
+                throw new StringPatternParserException(UNEXPECTED_CHARACTER_WANTED_OPENCURLYBRACE);%0A
         
@@ -1295,28 +1295,24 @@
 er.skip(1);%0A
-    
             
@@ -1392,135 +1392,8 @@
 s);%0A
-                %7D else%0A                    throw new StringPatternParserException(UNEXPECTED_CHARACTER_WANTED_OPENCURLYBRACE);%0A
     

Refactor DefineCharacterRangeState for greater clarity