package mc.sel.tokenizer;

import mc.sel.exception.TokenizerException;
import mc.sel.token.Token;
import mc.sel.token.TokenType;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Implementation of {@link Tokenizer} interface with regular expressions.
 *
 * @author Milan Crnjak
 */
public class RegexTokenizer implements Tokenizer {

    private List<TokenDescription> tokenDescriptions = new ArrayList<>();

    public RegexTokenizer() {
        initialize();
    }

    @Override
    public List<Token> tokenize(String input) throws TokenizerException {
        String s = input.trim();
        int totalLength = input.length();

        LinkedList<Token> tokens = new LinkedList<>();

        while (!s.isEmpty()) {
            int currentLength = s.length();

            boolean match = false;

            for (TokenDescription tokenDescription : tokenDescriptions) {
                Matcher m = tokenDescription.regex.matcher(s);

                if (m.find()) {
                    match = true;
                    String tokenSequence = m.group();
                    int startPos = totalLength - currentLength;
                    int endPos = startPos + tokenSequence.length() - 1;

                    tokens.add(new Token(tokenDescription.tokenType, tokenSequence, startPos, endPos));

                    s = m.replaceFirst("").trim();
                    break;
                }
            }

            if (!match) {
                throw new TokenizerException("Unknown symbol detected", totalLength - currentLength);
            }
        }

        return tokens;
    }

    private void initialize() {
        addTokenDescription("[+]", TokenType.PLUS);
        addTokenDescription("-", TokenType.MINUS);
        addTokenDescription("[*]", TokenType.MUL);
        addTokenDescription("/", TokenType.DIV);
        addTokenDescription("[(]", TokenType.LEFT_PAREN);
        addTokenDescription("[)]", TokenType.RIGHT_PAREN);
        addTokenDescription("and|&&", TokenType.AND);
        addTokenDescription("or|\\|\\|", TokenType.OR);
        addTokenDescription("gte|>=", TokenType.GTE);
        addTokenDescription("gt|>", TokenType.GT);
        addTokenDescription("lte|<=", TokenType.LTE);
        addTokenDescription("lt|<", TokenType.LT);
        addTokenDescription("eq|==", TokenType.EQUAL);
        addTokenDescription("true", TokenType.TRUE);
        addTokenDescription("false", TokenType.FALSE);
        addTokenDescription("null", TokenType.NULL);
        addTokenDescription("ne|!=", TokenType.NOT_EQUAL);
        addTokenDescription("!", TokenType.NOT);
        addTokenDescription("'((\\\\')|[^'])*?'", TokenType.STRING);
        addTokenDescription("[0-9]+\\.[0-9]+", TokenType.DOUBLE);
        addTokenDescription("[0-9]+", TokenType.INT);
        addTokenDescription("[a-zA-Z][a-zA-Z0-9_]+", TokenType.ID);
        addTokenDescription("\\.", TokenType.DOT);
        addTokenDescription(",", TokenType.ARGSEP);
    }

    private void addTokenDescription(String regex, TokenType tokenType) {
        tokenDescriptions.add(new TokenDescription(Pattern.compile("^(" + regex + ")"), tokenType));
    }

    private static class TokenDescription {
        TokenType tokenType;
        Pattern regex;

        TokenDescription(Pattern regex, TokenType tokenType) {
            this.regex = regex;
            this.tokenType = tokenType;
        }
    }


}

@@ -2075,11 +2075,19 @@
 on(%22
-and
+%5C%5Cb(and)%5C%5Cb
 %7C&&%22
@@ -2133,18 +2133,26 @@
 iption(%22
-or
+%5C%5Cb(or)%5C%5Cb
 %7C%5C%5C%7C%5C%5C%7C%22
@@ -2201,11 +2201,19 @@
 on(%22
-gte
+%5C%5Cb(gte)%5C%5Cb
 %7C%3E=%22
@@ -2259,18 +2259,26 @@
 iption(%22
-gt
+%5C%5Cb(gt)%5C%5Cb
 %7C%3E%22, Tok
@@ -2322,11 +2322,19 @@
 on(%22
-lte
+%5C%5Cb(lte)%5C%5Cb
 %7C%3C=%22
@@ -2380,18 +2380,26 @@
 iption(%22
-lt
+%5C%5Cb(lt)%5C%5Cb
 %7C%3C%22, Tok
@@ -2439,18 +2439,26 @@
 iption(%22
-eq
+%5C%5Cb(eq)%5C%5Cb
 %7C==%22, To
@@ -2502,20 +2502,28 @@
 iption(%22
-true
+%5C%5Cb(true)%5C%5Cb
 %22, Token
@@ -2563,21 +2563,29 @@
 iption(%22
+%5C%5Cb(
 false
+)%5C%5Cb
 %22, Token
@@ -2630,12 +2630,20 @@
 on(%22
-null
+%5C%5Cb(null)%5C%5Cb
 %22, T
@@ -2687,18 +2687,26 @@
 iption(%22
-ne
+%5C%5Cb(ne)%5C%5Cb
 %7C!=%22, To

Add word boundaries to keywords regex's