import java.util.Scanner;
import java.io.File;
import java.util.List;
import java.util.ArrayList;

/**
 * Class for reading, storing, and accessing valid codes.
 *   @author Dave Reed
 *   @version 9/19/16
 */
public class ValidCodesB2 {
    private List<String> codes;
    
    /**
     * Constructs a list of valid codes, read in from a file
     *   @param filename the file containing the valid codes, one per line
     */
    public ValidCodesB2(String filename) {
        this.codes = new ArrayList<String>();
        try {
            Scanner infile = new Scanner(new File(filename));
            
            while (infile.hasNext()) {
                String code = infile.next();
                this.codes.add(code.trim());
            }
            infile.close();
        }
        catch (java.io.FileNotFoundException e) {
            System.out.println("File not found.");
        }        
    }
    
    /**
     * Determines whether the specified code is contained in the valid list.
     *   @param code the code to be searched for
     *   @return true if code is contained in the list of valid codes
     */
    public boolean isValid(String code) {
        return this.codes.contains(code);
    }
    
    /**
     * Finds all valid codes that differ by one adjacent key only.
     *   @param code the code to be searched for
     *   @return a list of all valid codes that differ from code by one adjacent key.
     */
    public List<String> getPossibles(String code) {
        ArrayList<String> poss = new ArrayList<String>();
        for (String str : this.codes) {
            if (this.offByOne(str, code)) {
                poss.add(str);
            }
        }
        return poss;
    }
    
    /************************************************************************/
    
    /**
     * Helper method that determines if two codes differ by one adjacent key.
     *   @param code1 the first code
     *   @param code2 the second code
     *   @return true if code1 and code2 differ by exactly one adjacent key
     */
     private boolean offByOne(String code1, String code2) {
        if (code1.length() != code2.length()) {
            return false;
        }
        int diff = 0;
        for (int i = 0; i < code1.length(); i++) {
            char ch1 = code1.charAt(i);
            char ch2 = code2.charAt(i);
            if (ch1 != ch2) {
                if (diff == 0 && this.areAdjacent(ch1, ch2)) {
                    diff++;
                }
                else {
                    return false;
                }
            }
        }
        return (diff == 1);
    }
    
    /**
     * Determines if two keys are adjacent on the keypad.
     *   @param key1 the first key
     *   @param key2 the second key
     *   @return true if key1 and key2 are adjacent on the keypad
     */
    private boolean areAdjacent(char key1, char key2) {
        String keys = "123456789*0#";
        
        int index1 = keys.indexOf(key1);
        int index2 = keys.indexOf(key2);
        
        return ((Math.abs(index1-index2) == 3) ||
                (Math.abs(index1-index2) == 1 && index1/3 == index2/3)); 
    }
}