package picocli;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

class AbbreviationMatcher {
    static List<String> splitIntoChunks(String command) {
        List<String> result = new ArrayList<String>();
        int start = 0;
        for (int i = 0, codepoint; i < command.length(); i += Character.charCount(codepoint)) {
            codepoint = command.codePointAt(i);
            if (Character.isUpperCase(codepoint) || '-' == codepoint) {
                String chunk = makeCanonical(command.substring(start, i));
                if (chunk.length() > 0) {
                    result.add(chunk);
                }
                start = i;
            }
        }
        if (start < command.length()) {
            String chunk = makeCanonical(command.substring(start));
            if (chunk.length() > 0) {
                result.add(chunk);
            }
        }
        return result;
    }

    private static String makeCanonical(String str) {
        if ("-".equals(str)) {
            return "";
        }
        if (str.startsWith("-") && str.length() > 1) {
            int codepoint = str.codePointAt(1);
            char[] uppercase = Character.toChars(Character.toUpperCase(codepoint));
            return new String(uppercase) + str.substring(1 + Character.charCount(codepoint));
        }
        return str;
    }

    static String match(Set<String> set, String abbreviation) {
        if (set.contains(abbreviation)) { // return exact match
            return abbreviation;
        }
        List<String> abbreviatedKeyChunks = splitIntoChunks(abbreviation);
        List<String> candidates = new ArrayList<String>();
        for (String key : set) {
            List<String> keyChunks = splitIntoChunks(key);
            if (abbreviatedKeyChunks.size() > keyChunks.size()) {
                continue;
            } else if (!keyChunks.get(0).startsWith(abbreviatedKeyChunks.get(0))) { // first chunk must match
                continue;
            }
            int matchCount = 1, keyChunk = 1;
            for (int i = 1; i < abbreviatedKeyChunks.size(); i++) {
                boolean found = false;
                for (int j = keyChunk; j < keyChunks.size(); j++) {
                    if (keyChunks.get(j).startsWith(abbreviatedKeyChunks.get(i))) {
                        keyChunk = j + 1;
                        found = true;
                        break;
                    }
                }
                if (!found) { // not a candidate
                    break;
                }
                matchCount++;
            }
            if (matchCount == abbreviatedKeyChunks.size()) {
                candidates.add(key);
            }
        }
        if (candidates.size() > 1) {
            String str = candidates.toString();
            throw new IllegalArgumentException(abbreviation + " is not unique: it matches '" +
                    str.substring(1, str.length() - 1).replace(", ", "', '") + "'");
        }
        return candidates.isEmpty() ? abbreviation : candidates.get(0); // return the original if no match found
    }
}

@@ -1773,16 +1773,31 @@
      if 
+(matchKeyChunks
 (abbrevi
@@ -1813,37 +1813,349 @@
 unks
-.size() %3E keyChunks.size()) %7B
+, keyChunks)) %7B%0A                candidates.add(key);%0A            %7D%0A        %7D%0A        if (candidates.size() %3E 1) %7B%0A            String str = candidates.toString();%0A            throw new IllegalArgumentException(abbreviation + %22 is not unique: it matches '%22 +%0A                    str.substring(1, str.length() - 1).replace(%22, %22, %22', '%22) + %22'%22);
 %0A   
@@ -2155,32 +2155,34 @@
 %22);%0A        
+%7D%0A
         
 continue;%0A  
@@ -2165,38 +2165,327 @@
   %7D%0A        
-continue;%0A    
+return candidates.isEmpty() ? abbreviation : candidates.get(0); // return the original if no match found%0A    %7D%0A%0A    private static boolean matchKeyChunks(List%3CString%3E abbreviatedKeyChunks, List%3CString%3E keyChunks) %7B%0A        if (abbreviatedKeyChunks.size() %3E keyChunks.size()) %7B%0A            return false;%0A
         %7D el
@@ -2594,19 +2594,19 @@
     
-    continu
+return fals
 e;%0A 
@@ -2608,34 +2608,26 @@
 se;%0A        
-    %7D%0A    
+%7D%0A
         int 
@@ -2642,19 +2642,25 @@
 nt = 1, 
-key
+lastMatch
 Chunk = 
@@ -2658,28 +2658,24 @@
 hChunk = 1;%0A
-    
         for 
@@ -2722,16 +2722,30 @@
 e(); i++
+, matchCount++
 ) %7B%0A    
@@ -2752,20 +2752,16 @@
         
-    
 boolean 
@@ -2783,28 +2783,24 @@
             
-    
 for (int j =
@@ -2800,19 +2800,25 @@
 int j = 
-key
+lastMatch
 Chunk; j
@@ -2861,24 +2861,28 @@
         
-    
 if (
+found = 
 keyChunk
@@ -2957,15 +2957,17 @@
     
-    key
+lastMatch
 Chun
@@ -2981,50 +2981,8 @@
  1;%0A
-                        found = true;%0A    
     
@@ -2996,36 +2996,32 @@
          break;%0A
-    
                 
@@ -3030,33 +3030,26 @@
             
-    %7D%0A   
+%7D%0A
             
@@ -3044,25 +3044,24 @@
             
- 
 if (!found) 
@@ -3093,39 +3093,31 @@
             
-    
 break;%0A
-    
             
@@ -3126,24 +3126,26 @@
         
+%7D%0A
         
 matchCou
@@ -3140,545 +3140,57 @@
     
-matchCount++;%0A            %7D%0A            if (matchCount == abbreviatedKeyChunks.size()) %7B%0A                candidates.add(key);%0A            %7D%0A        %7D%0A        if (candidates.size() %3E 1) %7B%0A            String str = candidates.toString();%0A            throw new IllegalArgumentException(abbreviation + %22 is not unique: it matches '%22 +%0A                    str.substring(1, str.length() - 1).replace(%22, %22, %22', '%22) + %22'%22);%0A        %7D%0A        return candidates.isEmpty() ? abbreviation : candidates.get(0); // return the original if no match found
+return matchCount == abbreviatedKeyChunks.size();
 %0A   

Refactor AbbreviationMatcher