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 ValidCodesB1 {
    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 ValidCodesB1(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) {
        if (key1 == '1') {
            return "24".contains(key2+"");
        }
        else if (key1 == '2') {
            return "135".contains(key2+"");
        }
        else if (key1 == '3') {
            return "26".contains(key2+"");
        }
        else if (key1 == '4') {
            return "157".contains(key2+"");
        }
        else if (key1 == '5') {
            return "2468".contains(key2+"");
        }
        else if (key1 == '5') {
            return "2468".contains(key2+"");
        }
        else if (key1 == '6') {
            return "359".contains(key2+"");
        }
        else if (key1 == '7') {
            return "38*".contains(key2+"");
        }
        else if (key1 == '8') {
            return "5790".contains(key2+"");
        }
        else if (key1 == '9') {
            return "68#".contains(key2+"");
        }
        else if (key1 == '*') {
            return "70".contains(key2+"");
        }
        else if (key1 == '0') {
            return "8*#".contains(key2+"");
        }
        else if (key1 == '#') {
            return "90".contains(key2+"");
        }
        return false;
    }
}
