package it.matjaz.numerus.core;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Matjaž <a href="mailto:dev@matjaz.it">dev@matjaz.it</a>
 * <a href="http://matjaz.it">www.matjaz.it</a>
 */
public class RomanNumeral {

    private String symbols;

    /**
     * A big regex matching all syntactically correct roman numerals.
     *
     * Contains all possible cases of roman numerals. If a string does not match
     * this regex, then is not a roman numeral.
     */
    public final String CORRECT_ROMAN_SYNTAX_REGEX = "^(M{0,3})(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$";

    /**
     * Non roman characters.
     */
    public final String NON_ROMAN_CHARS_REGEX = "[^MDCLXVI]";

    /**
     * Four consecutive characters M or C or X or I.
     */
    private final String FOUR_CONSECUTIVE_TEN_LIKE_CHARS_REGEX = "(MMMM|CCCC|XXXX|IIII)";

    /**
     * Two non necessary consecutive characters D or L or V.
     */
    private final String TWO_SAME_FIVE_LIKE_CHARS_REGEX = "(D.*D|L.*L|V.*V)";

    public RomanNumeral() {
        this.symbols = "";
    }

    public String getSymbols() {
        return symbols;
    }

    public void setSymbols(String symbols) {
        String cleanSymbols = cleanSymbolsString(symbols);
        checkRomanSyntax(cleanSymbols);
        this.symbols = cleanSymbols;
    }

    private static String cleanSymbolsString(String symbols) {
        return symbols.replaceAll("\\s+", "").toUpperCase();
    }

    // from http://stackoverflow.com/questions/5705111/how-to-get-all-substring-for-a-given-regex
    private static String getAllMatches(String textToParse, String regex) {
        StringBuilder matches = new StringBuilder();
        Matcher matcher = Pattern.compile(regex).matcher(textToParse);
        while (matcher.find()) {
            matches.append(matcher.group());
        }
        return matches.toString();
    }

    private void checkRomanSyntax(String symbols) {
        if (symbols.isEmpty()) {
            throw new NumberFormatException("Empty roman numeral.");
        }
        if (symbols.length() >= 20) {
            throw new NumberFormatException("Impossibly long roman numeral.");
        }
        String matches;
        matches = getAllMatches(symbols, NON_ROMAN_CHARS_REGEX);
        if (!matches.isEmpty()) {
            throw new NumberFormatException("Non roman characters: " + matches);
        }
    }

}

@@ -1603,21 +1603,30 @@
  String 
-getAl
+findAllInterna
 lMatches
@@ -2219,47 +2219,134 @@
     
-String matches;%0A        matches = getAl
+if (!symbols.matches(CORRECT_ROMAN_SYNTAX_REGEX)) %7B%0A            String illegalChars;%0A            illegalChars = findAllInterna
 lMat
@@ -2383,16 +2383,20 @@
 REGEX);%0A
+    
         
@@ -2400,22 +2400,273 @@
    if (!
-matche
+illegalChars.isEmpty()) %7B%0A                throw new NumberFormatException(%22Non roman characters: %22 + illegalChars);%0A            %7D%0A            illegalChars = findAllInternalMatches(symbols, FOUR_CONSECUTIVE_TEN_LIKE_CHARS_REGEX);%0A            if (!illegalChar
 s.isEmpt
@@ -2664,32 +2664,36 @@
 rs.isEmpty()) %7B%0A
+    
             thro
@@ -2725,41 +2725,388 @@
 on(%22
-Non roman characters: %22 + matches
+Four consecutive: %22 + illegalChars);%0A            %7D%0A            illegalChars = findAllInternalMatches(symbols, TWO_SAME_FIVE_LIKE_CHARS_REGEX);%0A            if (!illegalChars.isEmpty()) %7B%0A                throw new NumberFormatException(%22Two D, L or V same characters: %22 + illegalChars);%0A            %7D%0A            throw new NumberFormatException(%22Generic roman numeral syntax error.%22
 );%0A 

Add correct roman numeral syntax check and some error cases