import java.io.*;
import java.nio.file.*;
import java.util.*;

public class Euler59 {

    private static class Options {
        String file = "resources/documents/0059_cipher.txt";
        boolean runCheckpoints = true;
    }

    private static boolean parseStringAfterPrefix(String arg, String prefix, StringBuilder value) {
        if (!arg.startsWith(prefix)) {
            return false;
        }
        String tail = arg.substring(prefix.length());
        if (tail.isEmpty()) {
            return false;
        }
        value.setLength(0);
        value.append(tail);
        return true;
    }

    private static boolean parseArguments(String[] args, Options options) {
        for (String arg : args) {
            if ("--skip-checkpoints".equals(arg)) {
                options.runCheckpoints = false;
                continue;
            }
            StringBuilder value = new StringBuilder();
            if (parseStringAfterPrefix(arg, "--file=", value)) {
                options.file = value.toString();
                continue;
            }
            System.err.println("Unknown argument: " + arg);
            return false;
        }
        return true;
    }

    private static List<Integer> parseCipherCsv(String payload) {
        List<Integer> values = new ArrayList<>();
        StringBuilder token = new StringBuilder();

        for (char c : payload.toCharArray()) {
            if (c >= '0' && c <= '9') {
                token.append(c);
            } else if (token.length() > 0) {
                values.add(Integer.parseInt(token.toString()));
                token.setLength(0);
            }
        }
        if (token.length() > 0) {
            values.add(Integer.parseInt(token.toString()));
        }

        return values;
    }

    private static String decryptWithKey(List<Integer> cipher, String key) {
        char[] out = new char[cipher.size()];
        for (int i = 0; i < cipher.size(); i++) {
            out[i] = (char)(cipher.get(i) ^ key.charAt(i % key.length()));
        }
        return new String(out);
    }

    private static int textScore(String text) {
        int score = 0;

        for (char c : text.toCharArray()) {
            int uc = (int)c;
            if (uc < 9 || uc > 126) {
                return -1000000;
            }
            if (c == ' ') {
                score += 3;
            }
            if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
                score += 2;
            }
        }

        int theCount = countOccurrences(text, " the ");
        int andCount = countOccurrences(text, " and ");
        int ofCount = countOccurrences(text, " of ");
        int toCount = countOccurrences(text, " to ");

        score += 40 * theCount;
        score += 25 * andCount;
        score += 20 * ofCount;
        score += 20 * toCount;

        return score;
    }

    private static int countOccurrences(String text, String needle) {
        int count = 0;
        int pos = 0;
        while ((pos = text.indexOf(needle, pos)) != -1) {
            count++;
            pos += needle.length();
        }
        return count;
    }

    private static long asciiSum(String text) {
        long total = 0;
        for (char c : text.toCharArray()) {
            total += (int)c;
        }
        return total;
    }

    private static long solve(String filePath) throws IOException {
        String content = Files.readString(Paths.get(filePath));
        List<Integer> cipher = parseCipherCsv(content);

        int bestScore = Integer.MIN_VALUE;
        String bestText = "";

        for (char a = 'a'; a <= 'z'; a++) {
            for (char b = 'a'; b <= 'z'; b++) {
                for (char c = 'a'; c <= 'z'; c++) {
                    String key = "" + a + b + c;
                    String text = decryptWithKey(cipher, key);
                    int score = textScore(text);
                    if (score > bestScore) {
                        bestScore = score;
                        bestText = text;
                    }
                }
            }
        }

        return asciiSum(bestText);
    }

    private static boolean runCheckpoints() {
        String key = "abc";
        String plain = "hello world";
        List<Integer> cipher = new ArrayList<>();
        for (int i = 0; i < plain.length(); i++) {
            cipher.add((int)plain.charAt(i) ^ key.charAt(i % key.length()));
        }

        if (!decryptWithKey(cipher, key).equals(plain)) {
            System.err.println("Checkpoint failed for XOR roundtrip");
            return false;
        }

        return true;
    }

    public static void main(String[] args) {
        Options options = new Options();
        if (!parseArguments(args, options)) {
            System.exit(1);
        }

        if (options.runCheckpoints && !runCheckpoints()) {
            System.exit(2);
        }

        try {
            System.out.println(solve(options.file));
        } catch (Exception ex) {
            System.err.println(ex.getMessage());
            System.exit(3);
        }
    }
}
