package lab01;

import java.util.*;
import java.io.*;

// WORD SEARCH SOLVER
//O objetivo deste trabalho é escrever um programa em JAVA para resolver Sopas de Letras
// A entrada do programa é um único ficheiro de texto contendo o puzzle e as palavras a encontrar

public class WSSolver {


    public enum Direction {
        UP,
        DOWN,
        LEFT,
        RIGHT,
        UPLEFT,
        DOWNLEFT,
        UPRIGHT,
        DOWNRIGHT;
    }
    
    public static int[] xDirections = {  0,  0,-1, 1, -1, -1,  1, 1}; //In order: UP DOWN LEFT RIGHT UPLEFT DOWNLEFT UPRIGHT DOWNRIGHT
    public static int[] yDirections = { -1,  1, 0, 0, -1,  1, -1, 1}; //In order: UP DOWN LEFT RIGHT UPLEFT DOWNLEFT UPRIGHT DOWNRIGHT


    public static int getRowDir(String direction) {
        switch (direction) {
            case "UP":
            case "UPLEFT":
            case "UPRIGHT":
                return -1;
            case "DOWN":
            case "DOWNLEFT":
            case "DOWNRIGHT":
                return 1;
            default:
                return 0;
        }
    }

    public static int getColDir(String direction) {
        switch (direction) {
            case "RIGHT":
            case "UPRIGHT":
            case "DOWNRIGHT":
                return 1;
            case "LEFT":
            case "UPLEFT":
            case "DOWNLEFT":
                return -1;
            default:
                return 0;
        }
    }

    public static int validateSoup(String fileName) throws FileNotFoundException{

        File filename = new File(fileName);
        Scanner file = new Scanner(filename);
        file.useDelimiter("\n");

        int linecount = 0;

        while (file.hasNextLine()){
            String activeLine = file.nextLine();

            if ((activeLine.contains(",") ||  activeLine.contains(";") || activeLine.contains(" ")) == true)
                break; 
            
            // soup
            //is it blank?
            if (activeLine.isBlank()){
                System.out.format("ERROR! Blank line at line %i found in the word soup. Please try again with the corrected file", linecount);
                System.exit(0);}


            //all uppercase?
            if (!activeLine.equals(activeLine.toUpperCase())){
                System.out.format("ERROR! All letters in the word soup must be uppercase. Please try again with the corrected file at line %i", linecount);
                System.exit(0);}
            
            //longer than 40chars?
            if (activeLine.length()>40){
                System.out.format("ERROR! All lines in the word soup must be smaller than 40 characters. Please try again with the corrected file at line %i", linecount);
                System.exit(0);}

            linecount++;

            System.out.println(activeLine);
        }

        file.close();
        
        //longer than 40 lines?
        if (linecount > 40){
            System.out.format("ERROR! The word soup must not be bigger than 40 lines (40x40). Please try again with the corrected file");
            System.exit(0);
        }

        return linecount;
    }


    public static char[][] soupMatrix (int soupSize, String fileName) throws FileNotFoundException {
        File filename = new File(fileName);
        Scanner file = new Scanner(filename);

        char[][] soup = new char [soupSize][soupSize];

        for (int i = 0; i < soupSize; i++) {
            String line = file.nextLine();
            for (int j = 0; j < soupSize; j++) {
                soup[i][j] = line.charAt(j);
            }
        }
        file.close();
        return soup;
    }


    //words


    public static List<String> getWords (String fileName) throws FileNotFoundException{
        File filename = new File(fileName);
        Scanner file = new Scanner(filename);

        List<String> words = new ArrayList<>();

        while (file.hasNextLine()){
            String activeLine = file.nextLine();
            if ((activeLine.contains(",") ||  activeLine.contains(";") || activeLine.contains(" ")) == true){
                String[] wordsInLine = activeLine.split("[;, ]");
                for(String word:wordsInLine){
                    words.add(word);
                }
            }
            else{ 
                continue;
            }
        }

        // if a word contains other, only the longer one will be in the list
        for (int j = 0; j < words.size() ; j++){
            for (int k = 0; k < words.size(); k++){
                if (!words.get(j).equals(words.get(k))){ 
                    if (words.get(j).contains(words.get(k))){
                        words.remove(k);
                    }
                }
            }
        }

        return words;            
    }

    public static void validateWords(List<String> words) throws FileNotFoundException{

        for (int i = 0; i < words.size() ; i++){

            // can only have lower case or mixed
            if (words.get(i).equals(words.get(i).toUpperCase())){
                System.out.format("ERROR! Words cannot appear in all uppercase. Please try again with the corrected file containing the word %s", words.get(i));
                System.exit(0);}

            // can only have letters"
            if (!words.get(i).matches("[a-zA-Z]+")){
                System.out.format("ERROR! Words can only contain letters. Please try again with the corrected file containing the word %s", words.get(i));
                System.exit(0);}

            // need more than 3 letters
            if (words.get(i).length() < 3){
                System.out.format("ERROR! Words must contain more than three letters. Please try again with the corrected file containing the word %s", words.get(i));
                System.exit(0);} 
        }
    }

    public static String[] findWord(char[][] soup, String word) {

        word = word.toUpperCase();
        int soup_size = soup.length;

        //String[] returnValues = new String[2];
        int direction = -1;
        
        for (int i = 0; i < soup_size; i++) {
            for (int j = 0; j < soup_size; j++) {
                if (soup[i][j] == word.charAt(0)) {
                    direction = checkAllDirections(soup, word, i, j);
                    if(direction!=-1)
                        return new String[]{++i + "," + ++j, Direction.values()[direction].toString()};
                    
                }
            }
        }

        System.err.format("ERROR! Word %s not found\n", word);
        System.exit(0);
        return null;
    }

        private static int checkAllDirections (char[][] soup, String word, int y, int x) {


            for (int i = 0; i < yDirections.length; i++) { //verify the 8 possible directions
                int charCounter;
                int nextX = x + xDirections[i];
                int nextY = y + yDirections[i];

                for (charCounter = 1; charCounter < word.length(); charCounter++) {
                    // If out of bound
                    if (nextX >= soup.length || nextX < 0 || nextY >= soup.length || nextY < 0)
                        break;

                    // If not matched
                    if (soup[nextY][nextX] != word.charAt(charCounter))
                        break;

                    //Continue moving
                    nextX += xDirections[i];
                    nextY += yDirections[i];
                }

                if (charCounter == word.length())
                    return i;
            }

            return -1;

        }


        public static void main(String args[]){

            String inFile = args[0] ;

            try{
                
                int soupLen = validateSoup(inFile);
                char[][] soup = soupMatrix(soupLen, inFile);
                List<String> words =  getWords(inFile);
                validateWords(words);
                System.out.println();
                System.out.println(words);
                System.out.println();


                //prepare matrix with solution
                char[][] solSoup = new char[soupLen][soupLen];
                for (int i = 0; i < soupLen; i++)
                    for (int j = 0; j < soupLen; j++)
                        solSoup[i][j] = '.';


                //print words and solutions
                for (String word : words){
                    String[] returnValues = findWord(soup, word);
                    String [] coords = returnValues[0].split("[,]");
                    String dir = returnValues[1];
                    int row = Integer.valueOf((coords[0]));
                    int col = Integer.valueOf((coords[1]));

                    for (int i = 0; i < word.length(); i++) {
                        solSoup[row-1][col-1] = (soup[row-1][col-1]);
                        row += getRowDir(dir);
                        col += getColDir(dir);
                    }
                    System.out.printf("%-10s %-5d %-4s %s\n", word, word.length(), returnValues[0], dir);

                }
                
                System.out.println();
                for (int i = 0; i < soupLen; i++) {
                    for (int j = 0; j < soupLen; j++){
                        System.out.print(solSoup[i][j]);
                    }
                    System.out.print("\n");
                }
            }
            catch (FileNotFoundException e){
                System.out.println("File not found, please try again with the correct input file.");
                System.exit(0);
            }  
            

    }
}


